Advanced CSS Techniques
Pseudo-elements
::before and ::after create virtual elements inside any element — no extra HTML needed:CSS
.badge::before {
content: "✓ ";
color: #22c55e;
}
.quote::before {
content: "\201C";
font-size: 3rem;
color: var(--accent);
position: absolute;
top: -10px;
left: -20px;
}content is required — without it, the pseudo-element doesn't render. Use content: "" for decorative pseudo-elements.Decorative lines and shapes
CSS
.section-title::after {
content: "";
display: block;
width: 60px;
height: 3px;
background: var(--accent);
margin-top: 0.5rem;
}The :has() selector
The "parent selector" CSS developers wanted for 20 years. It selects an element based on what's inside it:
CSS
/* Style a card differently if it contains an image */
.card:has(img) {
padding: 0;
}
/* Style a label when its input is focused */
label:has(input:focus) {
color: var(--accent);
}
/* Highlight form groups with invalid inputs */
.form-group:has(:invalid) {
border-left: 3px solid #dc2626;
}The :is() and :where() selectors
Reduce repetition in complex selectors:
CSS
/* Without :is() */
article h1, article h2, article h3 {
line-height: 1.2;
}
/* With :is() */
article :is(h1, h2, h3) {
line-height: 1.2;
}:where() works the same but has zero specificity — useful for resets and defaults that are easy to override.Logical properties
Instead of
left, right, top, bottom, use logical properties that work in any writing direction:CSS
.card {
margin-inline: auto; /* replaces margin-left + margin-right */
padding-block: 1rem; /* replaces padding-top + padding-bottom */
padding-inline: 1.5rem; /* replaces padding-left + padding-right */
border-inline-start: 3px solid var(--accent); /* replaces border-left */
}This makes your CSS work automatically in right-to-left (RTL) languages like Arabic or Hebrew.
Scroll snapping
Create smooth, snappy scroll behavior:
CSS
.carousel {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
gap: 1rem;
}
.carousel-item {
flex: 0 0 80%;
scroll-snap-align: start;
}Items snap into place when the user stops scrolling — no JavaScript carousel library needed.
Aspect ratio
Maintain consistent proportions:
CSS
.video-container {
aspect-ratio: 16 / 9;
width: 100%;
}
.avatar {
aspect-ratio: 1;
width: 48px;
border-radius: 50%;
object-fit: cover;
}Backdrop filter
Apply effects to the area behind an element:
CSS
.glass-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
}This creates the popular "glassmorphism" effect.
Color-mix() and modern color functions
CSS
.button:hover {
background: color-mix(in srgb, var(--accent), black 20%);
}This darkens the accent color by mixing it with 20% black — great for hover states without hardcoding a second color.
Nesting (native CSS)
Modern CSS now supports nesting, similar to Sass:
CSS
.card {
padding: 1rem;
border: 1px solid var(--border);
& h2 {
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
&:hover {
border-color: var(--accent);
}
& .card-footer {
border-top: 1px solid var(--border);
padding-top: 0.75rem;
}
}This keeps related styles grouped together and reduces repetition.
The @layer rule
Control the cascade order explicitly:
CSS
@layer reset, base, components, utilities;
@layer reset {
* { margin: 0; box-sizing: border-box; }
}
@layer utilities {
.hidden { display: none; }
.text-center { text-align: center; }
}Styles in later layers always beat styles in earlier layers, regardless of specificity. This eliminates many specificity wars.
Combining techniques in real components
Pseudo-elements shine for decorative affordances without extra HTML — icons, dividers, and quotation marks. Pair them with
position: relative on the parent when absolutely positioning ::before markers. :has() reduces JavaScript for simple conditional styling: highlight a fieldset when any child input is invalid, or adjust a gallery card when it contains a video. Logical properties future-proof layouts for internationalization without duplicate RTL stylesheets.Scroll snapping and
aspect-ratio solve problems that once required brittle resize listeners in JavaScript. Native nesting and @layer bring preprocessor ergonomics into standard CSS — use layers to order resets, components, and utilities so specificity stays flat. Reach for these features when they remove scripts or duplicate rules, not when a single class would suffice.Browser support and progressive enhancement
Most techniques here work in current evergreen browsers. For
:has() and container queries, verify targets against your audience. Provide simpler fallbacks: a card without :has(img) styling still works; it just uses uniform padding. Test backdrop-filter over varied backgrounds — low contrast can hurt readability. When adopting @layer, declare layer order once at the top of your entry file. Unlayered styles still beat layered ones, so migrate gradually: move reset and utilities into layers first, then components. Prefer shallow selector lists so future teammates can grep for class names and find styles quickly without untangling nested specificity.Key takeaway
Modern CSS is incredibly powerful.
:has() enables parent selection, scroll snapping replaces JavaScript carousels, native nesting reduces boilerplate, @layer tames the cascade, and logical properties make your CSS internationally friendly. These aren't experimental — they're supported in all modern browsers.