Syntax
[attr] { }
[attr="value"] { }
[attr^="value"] { }Examples
Presence and Exact Match
Selecting by whether an attribute exists, or matches exactly.
[disabled] { opacity: 0.5; } /* any element with a disabled attribute at all */
[type="submit"] { font-weight: bold; } /* inputs with type exactly "submit" */
[target="_blank"] { } /* links that open in a new tab */Pattern-Matching Attribute Values
Starts-with, ends-with, and contains matching.
a[href^="https://"] { } /* href starts with https:// */
a[href$=".pdf"] { } /* href ends with .pdf - useful for styling download links */
[class*="btn-"] { } /* class contains "btn-" anywhere within it */Parameters and values
- [attr] (presence): Matches if the attribute exists, regardless of value
- [attr="val"] (exact match): Matches only an exact value
- [attr^="val"] (starts with): Matches values beginning with the given string
- [attr$="val"] (ends with): Matches values ending with the given string
- [attr*="val"] (contains): Matches values containing the given string anywhere
Best practices
- Use [href$=".pdf"] style selectors to automatically style download links or external links without needing to add classes manually
- Use [disabled] or [aria-expanded="true"] to style based on state attributes, keeping styling in sync with actual element state
- Attribute selectors carry the same specificity as a class selector, so they combine predictably with other class-based rules
- Prefer classes for styling hooks that are purely presentational, and reserve attribute selectors for cases tied to genuine HTML semantics or state
At a glance
- Purpose
- Presentation and layout
- File extension
- .css
- Runs in
- Web browsers
- Usually used with
- HTML and JavaScript
Specifications & further reading
Related CSS documentation
Basic Selectors
CSS selectors target elements to style. Type selectors match by tag name, class selectors (.name) match elements with that class, ID selectors (#name) match a single unique element, and the universal selector (*) matches everything. Classes are the most commonly used selector for reusable styling.Combinators
Combinators define relationships between selectors. The descendant combinator (space) matches any nested element. The child combinator (>) matches only direct children. The adjacent sibling combinator (+) matches the very next sibling. The general sibling combinator (~) matches all following siblings.:has() - The Parent Selector
:has() matches an element if any of the selectors passed to it match something within it - effectively letting you style a parent based on its children or descendants, something CSS could never do natively before. It has broad, mainstream browser support as of 2026 and is safe to use in production.:is() and :where()
:is() and :where() both accept a list of selectors and match any of them, letting you write more compact selector groups. The key difference is specificity: :is() takes on the specificity of its most specific argument, while :where() always contributes zero specificity, making its rules trivially easy to override.
CSS selectors target elements to style. Type selectors match by tag name, class selectors (.name) match elements with that class, ID selectors (#name) match a single unique element, and the universal selector (*) matches everything. Classes are the most commonly used selector for reusable styling.Combinators
Combinators define relationships between selectors. The descendant combinator (space) matches any nested element. The child combinator (>) matches only direct children. The adjacent sibling combinator (+) matches the very next sibling. The general sibling combinator (~) matches all following siblings.:has() - The Parent Selector
:has() matches an element if any of the selectors passed to it match something within it - effectively letting you style a parent based on its children or descendants, something CSS could never do natively before. It has broad, mainstream browser support as of 2026 and is safe to use in production.:is() and :where()
:is() and :where() both accept a list of selectors and match any of them, letting you write more compact selector groups. The key difference is specificity: :is() takes on the specificity of its most specific argument, while :where() always contributes zero specificity, making its rules trivially easy to override.