CSS Attribute Selectors
Attribute selectors – used to select HTML elements based on the presence or value of their attributes. When you want to style elements not just by tag name or class, but by the attributes they hold—like type, href, target, or placeholder—attribute selectors let you do this with precision.
These selectors give you more control and flexibility by allowing you to match elements dynamically, based on specific attribute values, partial matches, or even just the existence of an attribute. This helps you write cleaner, more modular CSS without needing extra classes or IDs.
Attribute Selectors
Attribute selectors are used to select HTML elements based on the presence or value of their attributes.
Instead of targeting elements only by tag, class, or ID, attribute selectors allow you to apply styles based on:
- Whether an attribute exists
- What value it has
- Whether its value contains a specific word, part, prefix, or suffix
These selectors begin and end with square brackets [ ].
Types of Attribute Selectors
- [attribute] – Has Attribute Selector
- [attribute =”value”] – Exact Value Selector
- [attribute ~=”value”] – Whitespace Separated Value Selector
- [attribute|=”value”] – Hyphen Separated (Prefix) Selector
- [attribute^=”value”] – Begins With Selector
- [attribute$=”value”] – Ends With Selector
- [attribute*=”value”] – Contains Substring Selector
- [attribute=”value” i] – Case-Insensitive Exact Match Selector
- [attribute=”value” s] – Case-Sensitive Exact Match Selector

[attribute] – Has Attribute Selector
This selector selects any element that simply has a specific attribute, no matter what the value is.
Use this when you want to style all elements that contain a particular attribute, regardless of its value.
Syntax:
[required] {
border: 2px solid red;
}
<input type=”text” required> <!– Selected –>
<input type=”email”> <!– Not selected –>
- This selector matches all elements that include the attribute, regardless of its value.
- In this case, it applies styles to any input that has required, even if required=”required” is not written explicitly.
[attribute=”value”] – Exact Value Match Selector
Selects elements where the attribute’s value matches exactly with the specified value.
Use this when you need to target elements that have a specific and exact value in their attribute.
When you want to style elements like:
- <input type=”text”>
- <a target=”_blank”>
- <button type=”submit”>
Syntax:
[type=”text”] {
background-color: lightyellow;
}
<input type=”text”> <!– Selected –>
<input type=”email”> <!– Not selected –>
- Only those elements will be selected where type is exactly equal to “text”.
- No partial matches, no extra characters allowed.
[attribute~=”value”] – Whitespace-Separated Word Selector
Selects elements where the attribute contains the given word, as a separate word, separated by spaces.
This is commonly used with the class attribute, where multiple class names are separated by spaces.
Use the tilde ~ symbol between attribute name and value to select.
Syntax:
[class~=”box”] {
border: 1px solid black;
}
<div class=”box big”></div> <!– Selected –>
<div class=”big box”></div> <!– Selected –>
<div class=”bigbox”></div> <!– Not selected –>
- Matches only when the word box appears as a separate word in the value of the class attribute.
- Will not match bigbox or textbox, because the word is not standalone.
[attribute|=”value”] – Hyphen-Separated Prefix Selector
Selects elements where the attribute is either:
- Exactly equal to the given value, or
- Begins with that value followed by a hyphen (-)
It is mostly used with lang attributes for language-specific styling.
Use the vertical bar | between the attribute name and value to select.
Syntax:
[lang|=”en”] {
font-family: Arial, sans-serif;
}
<div lang=”en”>English Text</div> <!– Selected –>
<div lang=”en-US”>US English Text</div> <!– Selected –>
<div lang=”english”>Not Selected</div> <!– Not selected –>
- Will match if the attribute is “en” or starts with “en-“.
- It is perfect for language-specific formatting or localization-based styling.
[attribute^=”value”] – Starts With Selector
Selects elements where the attribute value starts with the given value.
Useful when you want to target all links or file paths that begin with a certain keyword, domain, or path prefix.
For things like:
- href that starts with https://
- src that starts with /images/
- data- attributes with known prefixes
Use the caret ^ symbol to select.
Syntax:
[href^=”https”] {
color: green;
}
<a href=”https://example.com”>Secure</a> <!– Selected –>
<a href=”http://example.com”>Not Secure</a> <!– Not selected –>
- The selector looks at the beginning characters of the value.
- Only attributes that start with the exact characters provided will be selected.
[attribute$=”value”] – Ends With Selector
Selects elements where the attribute value ends with the given value.
Normally, Used to select files or resources based on file extensions or ending strings.
- Selecting images ending with .png, .jpg, etc.
- Links ending with .pdf, .zip
Use the dollar sign $ symbol to select.
Syntax:
[src$=”.png”] {
border-radius: 10px;
}
<img src=”logo.png”> <!– Selected –>
<img src=”photo.jpg”> <!– Not selected –>
- Only values that end with .png will be matched.
- It is case-sensitive unless overridden.
[attribute*=”value”] – Contains Substring Selector
Selects elements where the attribute value contains the given substring, anywhere inside the value.
Useful for broad searches inside URLs, class names, or custom attributes.
- When you want to match part of a string
- Detect partial matches in values
Use the asterisk * symbol to select.
Syntax:
[href*=”login”] {
text-decoration: underline;
}
<a href=”/user/login”>Login</a> <!– Selected –>
<a href=”/login-help”>Help</a> <!– Selected –>
<a href=”/dashboard”>Not selected</a>
- It matches any attribute value that contains the substring “login”, no matter where it appears.
- It does not need to be a full word.
[attribute=”value” i] – Case-Insensitive Exact Match
Selects elements where the attribute value exactly matches, but it ignores case.
Use this when the attribute values may use uppercase or lowercase, but you want to match them all.
- User input fields
- File names with mixed casing
- Language codes, custom attributes
Add an i flag (for “ignore case”) after the value.
Syntax:
[type=”Text” i] {
background-color: lightblue;
}
<input type=”text”> <!– Selected –>
<input type=”TEXT”> <!– Selected –>
<input type=”TeXt”> <!– Selected –>
- This selector tells the browser: “Match the value exactly, but don’t care about uppercase or lowercase.”
- It works only in CSS Selectors Level 4 and modern browsers.
[attribute=”value” s] – Case-Sensitive Exact Match
Selects elements where the attribute value exactly matches, and is case-sensitive.
This is useful when you want to enforce strict case rules, such as matching a file name, command keyword, or attribute where case matters.
- Security settings
- Programming code examples
- Strict string comparisons
Add an s flag (for “case-sensitive”) after the value.
Syntax:
[type=”Text” s] {
color: red;
}
<input type=”Text”> <!– Selected –>
<input type=”text”> <!– Not selected –>
<input type=”TEXT”> <!– Not selected –>
- The selector will only match if the value is exactly the same, including upper and lower case letters.
- Not all browsers support the s flag yet — it’s part of CSS Selectors Level 4.
Most Important Questions
How does [class~=”btn”] behave differently from [class*=”btn”] and when would using one over the other cause bugs in large-scale UI systems?
- [class~=”btn”] matches only if “btn” is a complete word in the class attribute
- [class*=”btn”] matches if “btn” appears anywhere inside, even inside words like btn-primary, submit-btn.
If you use [class~=”btn”] assuming it will target btn-primary, you’re wrong. That’s a common bug in scalable systems where class names are constructed dynamically.
Is [type=”text”] more reliable or [type=”text” i] when building cross-browser accessible forms?
- [type=”text”] is case-sensitive by default in CSS, though HTML attributes are not case-sensitive.
- Browsers normalize type=”TEXT” to type=”text”, but if a developer mistakenly writes it as TEXT, TeXt, or Text, CSS without the i flag will not apply.
To make your forms more tolerant to human error and legacy code, [type=”text” i] is safer in production systems. It reduces cross-browser inconsistencies.
How do [lang|=”en”] and [lang^=”en”] differ in purpose when creating multi-language UI support?
- [lang|=”en”] matches “en” or any string that starts with en- (like en-US, en-GB). It is built specifically for language codes.
- [lang^=”en”] matches anything that starts with en, even english, enTest, entry, etc.
So, if you want to write a language-specific rule, use [lang|=”en”]. If you want to match a prefix without regard for hyphen structure, use [lang^=”en”].
In what scenario would [href^=”/”] fail to select expected links, and how would you fix it?
- [href^=”/”] selects links where href starts with a forward slash, meaning relative to the domain root.
- It will fail if:
- Links are absolute (https://example.com/page)
- Links use relative paths like ../about.html or just about.html
To select all internal links, you’d need a more inclusive strategy:
a[href^=”/”], a[href^=”./”], a[href^=”../”] {
color: orange;
}
Why does [src$=”.jpg”] sometimes fail on real projects even if image URLs end with .jpg?
Because URLs can contain query parameters or fragments:
<img src=”photo.jpg?version=2″>
<img src=”photo.jpg#section1″>
These technically don’t end with .jpg, so [src$=”.jpg”] will not match them.
Solution:
Use [src*=”.jpg”] instead. But this introduces a risk: it might match .jpg1234.
So depending on your needs, you might need to write a more complex selector or filter images server-side using JS.
Why is [data-status=”active”] more scalable and maintainable than .active in dynamic UI?
Using data-* attributes with attribute selectors is declarative, semantic, and easier to manage in large apps. Unlike classes:
- It keeps state information separate from visual styling
- JavaScript can update data-status cleanly (element.dataset.status = ‘inactive’)
- No need to add/remove multiple class names
Also, attribute selectors like [data-status=”active”] can be reused across components, not just limited to CSS class architecture.
What happens when [class=”btn”] is used instead of [class~=”btn”] or .btn, and why is it almost always wrong?
- [class=”btn”] matches only elements where the class is exactly “btn” — nothing else.
- If your element has class=”btn primary”, it will not be selected.
This is an extremely narrow and fragile selector. You almost never want [class=”…”] unless you’re targeting something very specific in inline HTML (like email templates).
Preferred choices:
- .btn (for general class)
- [class~=”btn”] (for space-separated words)
How would you write a selector that matches buttons with a data-action that starts with “save” but only when it also has required?
You must combine two attribute selectors:
button[data-action^=”save”][required] {
background-color: green;
}
This matches:
- Buttons with data-action=”save”, save-user, save-file
- And also must have required attribute
Why is [class*=”col-“] risky in utility-class-based frameworks like Bootstrap?
It will match:
- class=”col-md-6″
- class=”col-lg-4″
- But also possibly:
- collapse-toggle
- color-primary
- scroll-column
This selector will apply styles too broadly, unintentionally affecting elements not meant to be columns.
Safer choice:
[class^=”col-“] {
background-color: green; /* More specific */
}
Or even better, combine:
[class^=”col-“], [class*=” col-“] {
background-color: green; /* covers start and middle */
}
Can [class~=”box”][data-role=”admin”] be considered more specific than .box.admin? Explain.
Yes. [class~=”box”][data-role=”admin”] is more structurally specific, because:
- It explicitly checks the class attribute for an exact word (box)
- It requires the presence of a second attribute data-role=”admin”
In contrast, .box.admin simply checks that both classes exist — it doesn’t care about attribute structures or semantics.
In advanced UIs (like dashboard systems), using attribute selectors gives you better control over state, role, and behavior — separated from presentational class names.
Practice Tasks
Task 1:
Select all <input> fields that are required and add a red border.
input[required] {
border: 2px solid red;
}
Task 2:
Style all <a> tags that open in a new tab (target=”_blank”) with a special icon.
a[target=”_blank”]::after {
content: ” (external)”;
font-size: 0.8em;
}
Task 3:
Select all images that end in .png and give them rounded corners.
img[src$=”.png”] {
border-radius: 10px;
}
Task 4:
Select all <div> elements that have a class containing the word box.
div[class~=”box”] {
box-shadow: 0 0 10px gray;
}
Task 5:
Highlight all <input> fields where the name starts with “user”.
input[name^=”user”] {
background-color: #eef;
}
Task 6:
Hide all download links to .zip files.
a[href$=”.zip”] {
display: none;
}
Task 7:
Make any element with data-error=”true” show in red text and bold font.
[data-error=”true”] {
color: red;
font-weight: bold;
}
Task 8:
Style case-insensitive match for input types like TEXT, text, TeXt.
input[type=”text” i] {
border: 1px dashed black;
}