Attribute selectors let us select elements with certain attributes or attributes of a certain value.

HTML elements can have attributes, which are additional values for configuration or modifying their behaviour. There is a long list of HTML attributes and not all of them are applicable to all elements. Attributes that are not relevant to an element simply has no effect on it.

Regardless of whether the attribute is correctly applied or not, you can still select it via CSS. But it is really bad practice to have invalid HTML anywhere on your site, simply because different browsers interpret invalid HTML differently. Then you can't blame the browsers for rendering your site weird, they're simply trying to fill the gaps to account for your incorrect code.

Attribute values must be identifiers or strings. The specification is rather ambiguous as it states that the case-sensitivity of attribute names and values in selectors depends on the document language. Compounded by the fact that browser behaviour is inconsistent, your safest bet is to ensure that the case used is consistent between your CSS and HTML.

Attribute selectors can be matched in 7 ways (as of the CSS3 Specification):

Targets elements that contain the <attr> attribute, regardless of its value.
<div data-colour="green"></div>
<div data-colour="blue"></div>
<div data-colour="yellow"></div>
[data-colour] { /* some properties */ }
Targets elements where the <attr>'s value is exactly <val>.
<div data-colour="green"></div>
[data-colour="green"] { /* some properties */ }
Targets elements with the <attr> attribute whose value is a list of white space separated words, one of which must be <val>. <val> itself cannot contain white space, and neither can it be an empty string.
<div data-colour="green yellow blue"></div>
[data-colour~="green"] { /* some properties */ }
Targets elements with the <attr> attribute whose value is exactly <val> or starts with <val> immediately followed by a "-". Primary use-case being for language subcode matching, like "en", "en-US" and "en-UK".
<div data-colour="green-table"></div>
<div data-colour="green-chair"></div>
<div data-colour="green-bottle"></div>
[data-colour|="green"] { /* some properties */ }
A substring matching selector. Targets elements with the <attr> attribute whose value starts with <val>. <val> cannot be an empty string.
<div data-colour="greenish-yellow"></div>
<div data-colour="greengoblin"></div>
[data-colour^="green"] { /* some properties */ }
A substring matching selector. Targets elements with the <attr> attribute whose value ends with <val>. <val> cannot be an empty string.
<div data-colour="yellowish-green"></div>
<div data-colour="seagreen"></div>
[data-colour$="green"] { /* some properties */ }
A substring matching selector. Targets elements with the <attr> attribute whose value contains an instance of <val>. <val> cannot be an empty string.
<div data-colour="goblingreenish"></div>
[data-colour*="green"] { /* some properties */ }

Combining attribute selectors

Attribute selectors have the same specificity level as classes and pseudo-classes. For a handy reference, check out Specificity Calculator by Keegan Street. You can combine attribute selectors with other selectors like elements, classes or IDs.

div[data-colour="green"] {
  /* has a specificity of 11 */

.swatch[data-colour="green"] {
  /* has a specificity of 20 */

#tile25[data-colour="green"] {
  /* has a specificity of 110 */

You can also combine multiple attribute selectors to match specific patterns. For example, if you wanted to target only 2x images with alt text containing the word "green", your selector would look like this:

img[srcset~="2x"][alt*="green"] { /* some properties */ }

Trivia & Notes

In the initial draft version of the CSS2 specification back in 1997, there were only 3 ways to match selectors, [<attr>], [<attr>=<val>] and [<attr>~=<val>]. By the third published working draft, [<attr>|=<val>] was added as well. The Selectors Level 3 specification then added the substring matching selectors of [<attr>^=<val>], [<attr>=<val>] and [<attr>*=<val>].

Also, because attribute values are read as strings, you don't have to worry about escaping special characters to make them match, unlike classes or IDs. This means we are free to have things like these:

[data-emotion="😎"] {
  /* some properties */ 

[data-emotion="¯\_(ツ)_/¯"] {
  /* some properties */


Example #1

Being able to target elements based on attribute values can come in quite handy. Say your design calls for all links pointing to a particular URL be styled a certain way. Rather than applying a special class to each of those links, you can write a rule like this:

a[href=""]::after {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 3.5em;
  height: 3.5em;
  border: 1em solid rgba(0, 0, 0, 0.1);
  border-radius: 50%;
  content: '';
  opacity: 0;
  transition: transform 0.3s, opacity 0.3s;
  transform: translateX(-50%) translateY(-50%) scale(0.2);

a[href=""]::after {
  content: '\01F31F';
  border-width: 0.25em;
  transform: translateX(-50%) translateY(-50%) scale(0.8);

a[href=""]:hover::after {
  opacity: 1;
  transform: translateX(-50%) translateY(-50%) scale(1);
The CSS adds some special hover effects to links the Pixel Thoughts website, while all other links remain normal. You can see how this works out in the Live Demo below.

Example #2

We can also leverage the attribute for diagnostic purposes. Maybe you inherited an old code base that has some inline-styles in the markup. We can quickly target those elements and outline them in a bright colour to see where they are on the page, and whether it could be the inline-style that is causing an issue.

[style] {
  outline: 2px solid green; 

Live Demo

Example #1 Live Demo

Example #2 Live Demo

Browser Support

All the above combinators are supported in Chrome, Firefox, Safari, Opera 9.5+, Internet Explorer 9+, and on Android and iOS.

[caniuse feature="css-sel3"]

Further Reading