Question
I found this CSS selector and want to understand what the ~ character means:
.copy.check:checked ~ .content {
}
What does the ~ selector do in CSS, and how does it affect which elements are matched?
Short Answer
By the end of this page, you will understand that ~ in CSS is the general sibling combinator. It selects elements that share the same parent and appear after another matching element. You will also see how it differs from other selectors like > and +, and how it is commonly used for UI states such as toggles, checkboxes, and show/hide interactions.
Concept
In CSS, the ~ symbol is called the general sibling combinator.
It is used to select an element that:
- shares the same parent as another element
- appears later in the HTML
- matches the selector on the right side of
~
For example:
A ~ B
This means:
- find an element matching
A - then select every sibling matching
B - but only if those
Belements come afterA
So in your example:
.copy.check:checked ~ .content {
}
CSS is saying:
- find an element with classes
copyandcheck - that element must also be
:checked - then select any later sibling with the class
.content
This matters because CSS often styles elements based on the state of nearby elements. A common pattern is a checked checkbox controlling the appearance of a content panel, menu, or accordion section.
The ~ selector is useful because it lets one element influence later siblings without JavaScript, as long as the HTML structure supports it.
Mental Model
Think of siblings in CSS like items in the same row of seats.
- They must be in the same row = same parent element
~means “look to the right”- It can match one or many siblings after the starting element
So if one person in a row raises their hand (:checked), ~ lets you affect people sitting later in the same row.
It does not:
- look backward
- jump into children
- match elements with different parents
A simple way to remember it:
+= the next sibling only~= any later sibling
Syntax and Examples
The core syntax is:
A ~ B {
/* styles */
}
This selects all B elements that:
- are siblings of
A - come after
A - share the same parent
Basic example
<div>
<p class="note">First paragraph</p>
<p>Second paragraph</p>
<p>Third paragraph</p>
</div>
.note ~ p {
color: blue;
}
What gets selected?
- The second paragraph
- The third paragraph
The first paragraph is not selected, because it is the element on the left side of ~, not the right side.
Example with :checked
<div>
<input type="checkbox" class="copy check" checked>
<div class="content">Visible content</div>
</div>
.copy.check:checked ~ .content {
display: block;
}
This means:
- if the checkbox is checked
- then any later sibling with class
contentgets the styles
Example with hidden content toggle
<div class="panel">
<input type="checkbox" id="toggle">
<label for="toggle">Show details</label>
<div class="content">Extra details here</div>
</div>
.content {
display: none;
}
#toggle:checked ~ .content {
display: block;
}
When the checkbox is checked, the .content element becomes visible.
Step by Step Execution
Consider this HTML and CSS:
<div class="box">
<input type="checkbox" id="menu-toggle" checked>
<label for="menu-toggle">Open menu</label>
<div class="menu">Menu content</div>
</div>
.menu {
display: none;
}
#menu-toggle:checked ~ .menu {
display: block;
}
Here is what happens step by step:
- CSS first looks for an element matching
#menu-toggle:checked. - It finds the checkbox with
id="menu-toggle". - Because the checkbox is checked, the left side of the selector matches.
- CSS then looks for siblings after that checkbox.
- It checks whether any of those later siblings match
.menu. - The
<div class="menu">does match.menu. - The rule is applied, so
display: blockoverridesdisplay: none.
Important detail
The <label> is ignored by the selector because it does not match .menu.
Also important
If .menu appeared before the checkbox in the HTML, the selector would not work:
<div class="box">
<div class="menu">Menu content</div>
<input type="checkbox" id="menu-toggle" checked>
</div>
In this case, #menu-toggle:checked ~ .menu matches nothing, because ~ only works with later siblings.
Real World Use Cases
The ~ selector appears often in UI code where one element controls the style of another nearby element.
Common uses
- Checkbox-based show/hide panels
- Accordion sections
- Dropdown menus without JavaScript
- Mobile navigation toggles
- Form hints or validation messages
- Theme or preference switches
Example: show a help message
<div class="field">
<input type="checkbox" id="help-toggle">
<label for="help-toggle">Show help</label>
<p class="help">Your password should be at least 8 characters.</p>
</div>
.help {
display: none;
}
#help-toggle:checked ~ .help {
display: block;
}
Example: style later form elements
input.error ~ .message {
color: red;
}
If an input has class error, any later sibling .message can be styled as an error message.
Example: menu toggle pattern
Developers sometimes place a hidden checkbox before a menu and use :checked ~ .menu to open or close it with pure CSS.
Real Codebase Usage
In real projects, developers use ~ when HTML structure makes sibling-based styling convenient.
Common patterns
State-driven UI
A stateful element such as a checkbox, radio button, or input controls later siblings:
.sidebar-toggle:checked ~ .sidebar {
transform: translateX(0);
}
Conditional messages
A validation state on one element affects a related message block:
input:invalid ~ .error-message {
display: block;
}
Progressive enhancement
Even when JavaScript exists, CSS sibling selectors can handle simple visual state changes cleanly.
Why developers like it
- reduces simple JavaScript-driven toggles
- keeps state-based styling in CSS
- works well for small interactive components
Limits in real code
Developers must design the HTML structure carefully. If the controlled element is not a later sibling with the same parent, ~ will not help.
Because of that, teams often combine ~ with:
- utility classes
- component-based markup
- hidden checkboxes for toggle state
- semantic labels for accessible interactions
Common Mistakes
Here are the most common beginner mistakes with ~.
1. Expecting it to match previous siblings
Broken example:
<div>
<div class="content"></div>
<input type="checkbox" id="toggle" checked>
</div>
#toggle:checked ~ .content {
display: block;
}
This does not work because .content comes before #toggle.
Fix
Place .content after the checkbox, or use a different structure.
2. Forgetting that both elements need the same parent
Broken example:
<div>
<input type="checkbox" id="toggle" checked>
</div>
<section>
<div class="content"></div>
</section>
#toggle:checked ~ .content {
display: block;
}
This fails because the checkbox and .content are not siblings.
Fix
Make sure both are under the same parent.
3. Confusing ~ with +
A + B
matches only the immediately next sibling.
A ~ B
matches any later matching sibling.
4. Thinking it selects children
Broken expectation:
.parent ~ .child
This does not mean “a child inside .parent.” It means “a sibling after .parent.”
Fix
Use descendant selectors for children, such as:
.parent .child
5. Forgetting that :checked only applies to checkable elements
:checked works with elements like:
- checkbox inputs
- radio inputs
- some option elements
It does not apply to ordinary <div> elements.
Comparisons
Here is how ~ compares to related CSS combinators.
| Selector | Meaning | Must be same parent? | Must come after? | Matches how many? |
|---|---|---|---|---|
A B | descendant selector | No | No | any matching descendants |
A > B | child selector | Yes, direct child | No | direct children only |
A + B | adjacent sibling selector | Yes | Yes | next sibling only |
A ~ B | general sibling selector | Yes | Yes | all later matching siblings |
Example comparison
<div>
<h2 class="title">Title</h2>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
<span>Span</span>
</div>
.title + p {
color: red;
}
Only Paragraph 1 is selected.
.title ~ p {
color: blue;
}
Both Paragraph 1 and Paragraph 2 are selected.
When to use which
- Use
+when you need the very next sibling. - Use
~when the target may be several positions later. - Use
>when selecting direct children. - Use a space selector when selecting nested descendants.
Cheat Sheet
A ~ B {
/* styles for later sibling(s) B */
}
Rules
~is the general sibling combinatorBmust come afterAin the HTMLAandBmust share the same parent- it can match multiple later siblings
- it does not match previous siblings
- it does not match children unless they are also siblings in the right place
Common example
input:checked ~ .content {
display: block;
}
Quick comparison
A + B→ next sibling onlyA ~ B→ any later matching siblingA > B→ direct childA B→ any descendant
Easy memory trick
+= next one~= later ones
FAQ
What is the ~ symbol called in CSS?
It is called the general sibling combinator.
What does A ~ B mean in CSS?
It selects every B element that is a sibling of A, comes after A, and has the same parent.
What is the difference between + and ~ in CSS?
+ selects only the immediately next sibling. ~ selects any matching siblings that appear later.
Does the ~ selector work on previous siblings?
No. It only works on siblings that come after the first element in the HTML.
Do the elements need the same parent for ~ to work?
Yes. The two elements must be siblings under the same parent element.
Why is :checked ~ .content common in CSS?
It is often used to show, hide, or restyle content based on a checkbox or radio button state without JavaScript.
Can ~ select multiple elements?
Yes. If several later siblings match the selector on the right, all of them can be selected.
Can I use ~ with classes, IDs, and pseudo-classes?
Yes. You can combine it with normal selectors like classes, IDs, attributes, and pseudo-classes such as :checked or :focus.
Mini Project
Description
Build a simple FAQ toggle using only HTML and CSS. This project demonstrates how the ~ general sibling selector can reveal content when a checkbox is checked. It is a practical example of state-based styling without JavaScript.
Goal
Create a toggleable answer panel that appears when the user checks a checkbox.
Requirements
- Add a checkbox input to control the panel state.
- Add a label that the user can click.
- Add a content block that starts hidden.
- Use the
:checked ~ .contentpattern to show the content. - Keep the checkbox, label, and content under the same parent element.
Keep learning
Related questions
Angular ngClass Conditional Class Binding Explained
Learn how to use Angular ngClass for conditional classes, fix common binding mistakes, and understand why this template error happens.
CSS :not() Selector for Elements Without a Class or Attribute
Learn how to use the CSS :not() selector to target elements that do not have a specific class or attribute, with examples and common pitfalls.
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.