CSS Grid: The Complete Guide
Everything you need to know about CSS Grid in one comprehensive guide.
Finally, a parent selector in CSS! Style elements based on what they contain. It's like magic, but better - it's native CSS.
The :has() pseudo-class lets you select an element if it contains another element that matches a selector. Think of it as "select a parent that has a specific child" - something we've been dreaming about for years!
// JavaScript required!
const cards = document.querySelectorAll('.card');
cards.forEach(card => {
if (card.querySelector('.badge')) {
card.classList.add('has-badge');
}
});
/* Pure CSS! */
.card:has(.badge) {
border: 2px solid gold;
background: linear-gradient(
to right,
rgba(255, 215, 0, 0.1),
transparent
);
}
/* Show error only when input is invalid */
.form-field:has(input:invalid:not(:placeholder-shown)) {
.error-message {
display: block;
color: #ef4444;
}
input {
border-color: #ef4444;
}
}
/* Green tick for valid inputs */
.form-field:has(input:valid:not(:placeholder-shown))::after {
content: '✓';
color: #10b981;
position: absolute;
right: 1rem;
top: 2.5rem;
}
Master modern CSS with this brilliant course
Start your journey with the fundamentals
Take your skills to the next level
/* Style cards that contain badges */
.product-card:has(.badge) {
border: 2px solid var(--badge-colour);
position: relative;
overflow: visible;
}
/* Different styles for different badges */
.product-card:has(.badge.new) {
--badge-colour: #10b981;
background: linear-gradient(135deg,
rgba(16, 185, 129, 0.1) 0%,
transparent 50%);
}
.product-card:has(.badge.sale) {
--badge-colour: #ef4444;
animation: pulse 2s infinite;
}
/* Style nav items that have dropdowns */
nav li:has(.dropdown) {
position: relative;
}
/* Add dropdown arrow to items with submenus */
nav li:has(.dropdown) > a::after {
content: ' ▾';
font-size: 0.8em;
}
/* Show dropdown on hover */
nav li:has(.dropdown):hover .dropdown {
display: block;
animation: slideDown 0.3s ease-out;
}
/* Style figures that have captions differently */
.gallery-item:has(figcaption) {
background: var(--colour-surface);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-md);
}
.gallery-item:not(:has(figcaption)) {
border-radius: var(--radius-md);
opacity: 0.9;
}
/* Blur images without captions on hover */
.gallery-grid:hover .gallery-item:not(:has(figcaption)) {
filter: blur(2px);
opacity: 0.7;
}
Everything you need to know about CSS Grid in one comprehensive guide.
Quick video tutorial on mastering Flexbox layouts.
Discover how to create stunning visuals using only CSS.
/* Different layouts for articles with media */
.blog-article:has(img, video) {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1.5rem;
align-items: start;
}
.blog-article:has(video) {
background: linear-gradient(
135deg,
rgba(147, 51, 234, 0.1) 0%,
transparent 50%
);
border-left: 4px solid #9333ea;
}
/* Articles without media stay simple */
.blog-article:not(:has(img, video)) {
padding: 1.5rem;
background: var(--colour-surface);
}
/* Style when followed by specific element */
h2:has(+ p) {
margin-bottom: 0.5rem;
}
/* Grid when contains both images and text */
.container:has(img):has(p) {
display: grid;
grid-template-columns: 1fr 1fr;
}
/* Different layout based on child count */
.gallery:has(> :nth-child(5)) {
grid-template-columns: repeat(3, 1fr);
}
/* Single column if only one child */
.list:has(> :only-child) {
max-width: 600px;
margin: 0 auto;
}
/* Highlight row with checked checkbox */
tr:has(input:checked) {
background: rgba(16, 185, 129, 0.1);
}
/* Disable form if any field is invalid */
form:has(input:invalid) button[type="submit"] {
opacity: 0.5;
cursor: not-allowed;
}
/* Base styles for all browsers */
.card {
border: 1px solid #e5e7eb;
}
/* Enhancement for browsers with :has() support */
@supports selector(:has(*)) {
.card:has(.premium-badge) {
border-color: gold;
background: linear-gradient(
135deg,
rgba(255, 215, 0, 0.1) 0%,
transparent 50%
);
}
}
Keep :has() selectors simple. Complex selectors can impact performance, especially with many elements.
:has() doesn't increase specificity by itself - it's the selectors inside that count.
Always test in browsers that don't support :has() to ensure graceful degradation.
Like a good cuppa, :has() makes everything better. Use it wisely and your CSS will thank you!