::checkmark

Sunkanmi Fafowora on
##description##
Ad
`, } ); } })();

Experimental: Check browser support before using this in production.

The CSS ::checkmark pseudo-element checks whether an element is selected or not, allowing us to style its state. It’s supposed to target selected form elements, including checkbox, radio, and option elements in select menus, although it currently only works with select menus.

For example, we can display an arrow character next to an option if its checkbox is selected:

option::checkmark {
  content: "⮕";
}

This way, when the select menu is open and you select an option from the list, the selected option is considered “checked” and the style is applied to that specific option.

An open select menu with programming languages as options, showing a custom green right arrow to the left of the selected option.
Here’s a screenshot showing the custom checkmark if your browser doesn’t support ::checkmark yet.

We used to resort to creative workarounds using <div> and <span> elements to achieve this same result. But that’s why we have the ::checkmark pseudo-element: we can hook directly into the form element’s checked state and style accordingly.

The CSS ::checkmark pseudo-element is defined in the CSS Form Control Styling Level 1 specification.

Syntax

The syntax is pretty straightforward:

<element-list>::checkmark {}

In short, all that’s saying is we declare a selector and tack on the pseudo-element before we start adding styles to the rule. And by “<element-list>” we’re referring specifically to these form elements:

However, it currently only works for the <option> element as of this writing. That means we can expect ::checkmark to work on other form inputs in the future.

Values

The ::checkmark pseudo-element is typically used to set styles on an element’s content property.

/* emoji */
option::checkmark {
  content: "✅";
}

/* unicode symbol */
option::checkmark {
  content: "\2713";
}

/* background image */
option::checkmark {
  content: "";
  background: url("./animated-check.gif") no-repeat center center;
}

Even though we can apply styles to other properties, notice that the content property is still required because we’re working directly with a pseudo-element which is generated content. Additionally, we have the element also requires that:

So, if you’re wondering if we can also apply the ::checkmark pseudo-element to other pseudo-element’s — such as ::before and ::after — sadly, that is not the case.

Basic usage

What’s great about styling a form element’s checkmark is we can insert all kinds of interesting visual effects to it, including emoji, texts, and even animated GIFs. We can throw almost anything into a checkmark!

Here’s an example. If an option is selected, it is considered “checked” and gets a checkmark. That checkmark is generated automatically for us by the browser.

That checkmark looks great and all, but we can customize it, thanks to ::checkmark! That’s where we can make our own checkmark with an emoji, image, or any string of text. Here it is with an animated GIF:

Heads up! GIFs are not the most performant or accessible thing in the world. If we have to use them, it might be better in a disclosure widget. Our friend John Rhea, also has a clever way to handle it.

Or, hey, why not emoji instead?

While we’re here, why not try adding a little animation to the mix. We can totally apply animation keyframes to our custom checkmark:

option {
  animation: heartBounce 2s ease-in-out infinite;
}

@keyframes heartBounce {
  0%,
  100% { transform: translateY(0px) scale(1); }
  25% { transform: translateY(1px) scale(1.05); }
  50% { transform: translateY(-2px) scale(1.1) rotate(3deg); }
  75% { transform: translateY(1px) scale(1.05) rotate(-2deg); }
}

Better checkmarks styling

Styling select menus has never been easy. It’s getting easier thanks to new work being done on a fully-customizable select element, but that’s still in development as I’m writing this. It is, however, implemented in Chrome as an experimental feature, so let’s assume it works everywhere for a moment and see how we can get more creative with our checkmarks.

Here’s an example. This future demo uses an emoji for a checkbox and would only display it once browser support comes through:

Style things that aren’t checked

Did you know you can style select option items that are not checked? This works simply by styling the checkmark using the :not() pseudo-class:

/* If there is a checkmark */
option::checkmark {
  content: "✔";
}

/* If there is NOT a checkmark */
option:not(:checked) {
  &::checkmark {
    visibility: visible;
    content: "x";
  }
}

Notice how the visibility of the checkmark has to be set to visible? The checkmark sets the visibility of the remaining options to hidden by default. That means we have to manually set it to visible for this to work.

An open select menu with programming languages as options. The unselected options have a green X next to them while the currently selected option has a green checkmark.

Demo

Here’s one more demo of a typical use case where you might use ::checkmark. Select a font size from the menu and the size of the text in the demo will adjust accordingly.

Specification

The CSS ::checkmark pseudo-element is defined in the CSS Form Control Styling Level 1 specification, which is currently in Editor’s Draft status. That means the information can change between now and when it officially becomes a formal Candidate Recommendation.

Browser support

ChromeEdgeFirefoxSafari
135135NoNo

It’s probably best to avoid using ::checkmark until there is full support for it in all browsers. If you were to use it without full support, the browsers that lack support will not display any checkmark at all, which could lead to a confusing (or, worse, inaccessible) user experience. If you have to use it, consider wrapping it in a @supports rule instead.