Question
CSS Previous Sibling Selector: What Exists and How to Work Around It
Question
In CSS, the adjacent sibling combinator (+) selects the next sibling element that comes immediately after another element.
For example:
.first + .second {
color: red;
}
This targets .second when it appears immediately after .first.
Is there an equivalent CSS selector that can target the previous adjacent sibling instead of the next one?
Short Answer
By the end of this page, you will understand how CSS sibling selectors work, why CSS does not provide a traditional previous sibling selector, and which practical alternatives developers use instead, including restructuring selectors, using parent-aware selectors like :has(), and adjusting HTML when needed.
Concept
CSS selectors are designed to match elements based on document structure, and most traditional selectors work forward through the DOM, not backward.
A sibling selector like A + B means:
- find an element
A - then match
Bif it comes immediately afterA
Similarly, A ~ B matches later siblings, not earlier ones.
Why there is no classic previous sibling selector
In standard CSS selector history, there has not been a direct combinator that means "select the element immediately before this one." In other words, CSS has long supported:
- child relationships
- descendant relationships
- following sibling relationships
But not a simple backward sibling lookup like B - A or B < A.
This matters because beginners often expect CSS to work like "find this element, then walk backward to its previous sibling," but selector matching has traditionally been designed around matching an element based on what comes before it in the document, not selecting an earlier element from a later one.
What you can do instead
Common alternatives include:
- Rewrite the selector so you style the later element instead of the earlier one
- Change the HTML structure so the element you want to style can be targeted naturally
- in modern CSS to select an element based on what comes after it
Mental Model
Think of CSS selectors like reading a line of people from left to right.
Traditional sibling selectors let you say:
- "Select the person standing after Alex"
- "Select everyone standing somewhere after Alex"
But they do not let you directly say:
- "Select the person standing before Taylor"
At least, not with the older basic combinators.
With modern :has(), you can do something closer to:
- "Select the person who has Taylor standing right after them"
So instead of selecting backward directly, you select the earlier element by describing what follows it.
Syntax and Examples
Core sibling selectors
/* Adjacent next sibling */
A + B {
/* styles */
}
/* Any following sibling */
A ~ B {
/* styles */
}
These only target elements that come after A.
Example: next sibling works
<label>Email</label>
<input type="email" />
label + input {
border: 2px solid steelblue;
}
This styles the input because it comes immediately after the label.
What does not exist in classic CSS
There is no traditional selector like this:
/* Not valid CSS */
input - {
: red;
}
Step by Step Execution
Consider this HTML and CSS:
<p class="title">Profile</p>
<p class="note">Required</p>
.title + .note {
color: red;
}
Step by step
-
CSS looks for an element with class
.title. -
It finds:
<p class="title">Profile</p> -
It then checks the immediately following sibling.
-
The next sibling is:
<p class="note">Required</p> -
Because that sibling matches , the rule applies.
Real World Use Cases
Form styling
You may want to style a label differently if a specific input follows it.
label:has(+ input[required]) {
font-weight: bold;
}
This can visually mark labels for required fields.
Lists and menus
You might want to style an item that comes before a special status element.
li:has(+ li.active) {
border-bottom: 1px solid orange;
}
Cards and layout spacing
Sometimes spacing depends on what follows.
.card:has(+ .card.featured) {
margin-bottom: 2rem;
}
Validation and helper text
A field wrapper can be styled if an error message appears after it.
.field-label:has(+ .error-message) {
: crimson;
}
Real Codebase Usage
In real projects, developers usually do not look for a direct previous sibling selector first. Instead, they choose one of a few common patterns.
1. Restructure the selector
Style the later sibling instead of the earlier one when possible.
.label + .input {
margin-top: 0.25rem;
}
This is often the simplest and most maintainable option.
2. Use wrapper elements
Developers often group related elements inside a parent so they can target the group instead of a previous sibling.
<div class="field required">
<label>Name</label>
<input />
</div>
.field.required label {
font-weight: bold;
}
This is common in component-based codebases.
3. Use :has() for parent-aware or relationship-aware styling
Common Mistakes
Mistake 1: Expecting the left side of + to be styled
Broken expectation:
.first + .second {
color: red;
}
Beginners sometimes think both elements are involved equally. They are not. The element that gets styled is .second.
How to avoid it:
- Read selectors from right to left for the matched element
- Treat the left side as a condition
Mistake 2: Trying to invent a previous sibling operator
Broken code:
.second < .first {
color: red;
}
This is not valid CSS.
How to avoid it:
- Use valid combinators only: space,
>,+,~ - Use
:has()if you need to style an earlier element based on what follows
Mistake 3: Forgetting browser support for :has()
Comparisons
| Selector or approach | What it does | Can it target a previous sibling? | Notes |
|---|---|---|---|
A + B | Selects B immediately after A | No | Adjacent next sibling only |
A ~ B | Selects any later B sibling after A | No | General following sibling |
A > B | Selects direct child B of A | No | Parent-child relationship, not siblings |
A B |
Cheat Sheet
Quick rules
A + Bselects the next immediate siblingBA ~ Bselects later siblingsB- Traditional CSS has no direct previous sibling combinator
- The matched element is usually the rightmost part of the selector
:has()can often simulate previous sibling logic
Common syntax
/* Next adjacent sibling */
A + B {}
/* Any following sibling */
A ~ B {}
/* Style earlier sibling if followed by B */
A:has(+ B) {}
Examples
label + input {
border-color: blue;
}
label:has(+ input[required]) {
font-weight: bold;
}
Remember
FAQ
Is there a previous sibling selector in CSS?
Not as a traditional combinator. CSS has next-sibling selectors like + and ~, but not a direct previous sibling operator.
How can I style a previous sibling in CSS?
Use :has() when available, for example:
label:has(+ input) {
color: red;
}
This styles the earlier element by checking what comes after it.
Why does CSS support next siblings but not previous siblings?
Historically, selector matching was designed around forward document relationships and conditions on later matched elements, not backward traversal with a basic combinator.
Is :has() the same as a previous sibling selector?
Not exactly. It is more flexible. It lets you select an element based on whether it contains or is followed by something matching a condition.
Should I use :has() in production?
You can if your target browsers support it. Always verify compatibility for your audience before depending on it.
What is the best fallback if I cannot use :has()?
Usually one of these:
- add a class in the HTML
Mini Project
Description
Build a small form section where labels change appearance depending on the input that follows them. This demonstrates the practical closest equivalent to a previous sibling selector using modern CSS with :has() and also shows a clean HTML structure for form styling.
Goal
Create a form where labels for required fields are styled automatically when they are immediately followed by a required input.
Requirements
- Create at least two label and input pairs
- Make one input required and one optional
- Style the label of the required input differently using CSS
- Use
:has()with the adjacent sibling combinator - Keep the HTML simple and valid
Keep learning
Related questions
CSS Font Scaling Relative to Container Size: %, em, rem, vw, and Responsive Text
Learn how CSS font scaling really works and how to make text responsive using %, em, rem, vw, clamp(), and media queries.
CSS Parent Selector Explained: Selecting a Parent <li> from an <a>
Learn whether CSS has a parent selector, how :has() works, and practical alternatives for styling a parent li from a child anchor.
CSS Transitions on display: Why They Don't Work and How to Fade Elements In
Learn why CSS cannot transition the display property and how to create smooth fade-in dropdown menus using opacity and visibility.