Skip to content
AgileLogics
4 min read

Attribute Selectors

Prerequisites: Basic selectors

Attribute selectors match elements by their attributes:

Selector Matches when the attribute...
[disabled] exists
[type="email"] equals email exactly
[class~="card"] is a space-separated list containing card
[lang|="en"] is en or starts with en-
[href^="https:"] starts with https:
[href$=".pdf"] ends with .pdf
[href*="example"] contains example
[type="email" i] equals, case-insensitively
input[type="email"],
input[type="password"] {
  border: 2px solid #888;
}

a[href$=".pdf"]::after {
  content: " (PDF)";
}

a[href^="http"]:not([href*="cornerbakery.example"]) {
  text-decoration-style: dashed;
}

[aria-expanded="true"] {
  background: #eef;
}

[data-state="sold-out"] {
  opacity: 0.5;
}
  • ::after with content adds text after an element — covered with pseudo-elements in Level 2.
  • Styling by ARIA and data-* attributes keeps visual state tied to the real state in the HTML — when JavaScript flips aria-expanded, the style follows automatically.

Try it

<style>
  a { color: black; }
  a[href^="https:"] { color: green; }
  a[href$=".pdf"] { color: red; }
</style>
<a id="a1" href="/about">About</a>
<a id="a2" href="https://example.com">External</a>
<a id="a3" href="https://example.com/menu.pdf">Menu</a>
<script>
  for (const id of ["a1", "a2", "a3"]) {
    console.log(id, getComputedStyle(document.getElementById(id)).color);
  }
</script>
a1 rgb(0, 0, 0)
a2 rgb(0, 128, 0)
a3 rgb(255, 0, 0)

The third link matches both attribute rules. They're equally specific, so the one that comes later wins — red. That's the cascade, next.