Modern CSS Techniques Every Developer Should Know

CSS has evolved dramatically. Features that required JavaScript or complicated workarounds are now built into the language. Here are the techniques worth learning today.
CSS Custom Properties
CSS variables let you define values once and reuse them everywhere. When you change the variable, everything updates.
Start With Colors and Spacing
Define your colors and spacing scale as custom properties. This makes theming and consistency easy.
:root {
/* Colors */
--color-primary: #00A693;
--color-text: #1a1a1a;
--color-muted: #6b7280;
/* Spacing scale */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 2rem;
--space-xl: 4rem;
}
.button {
background: var(--color-primary);
padding: var(--space-sm) var(--space-md);
}
Dark Mode With Variables
Override variables in a dark mode class to switch themes instantly.
:root {
--bg: #ffffff;
--text: #1a1a1a;
}
.dark {
--bg: #0a0a0a;
--text: #fafafa;
}
body {
background: var(--bg);
color: var(--text);
}
Flexbox for Everything
Flexbox solves layout problems that used to require hacks. The most common patterns take just a few lines.
Center Anything
These three lines center any element both horizontally and vertically.
.center {
display: flex;
justify-content: center;
align-items: center;
}
Common flexbox patterns:
/* Space items evenly */
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
}
/* Stack with gap */
.card-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Wrap cards in a row */
.grid {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
CSS Grid for Layouts
Grid handles two-dimensional layouts that flexbox struggles with.
Perfect Card Grids
This pattern creates responsive card grids without media queries.
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
This creates as many 300px+ columns as will fit, automatically adjusting on different screen sizes.
Grid Areas for Page Layout
Name your grid areas for readable, maintainable layouts.
.page {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 250px 1fr;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
The :has() Selector
The :has() selector lets you style a parent based on its children. This was impossible in CSS before.
Browser Support
Check browser support before using in production. Works in all modern browsers as of 2024, but older browsers do not support it.
/* Style a card differently if it has an image */
.card:has(img) {
padding-top: 0;
}
/* Highlight form field when input is focused */
.form-field:has(input:focus) {
border-color: var(--color-primary);
}
/* Show error styling when validation fails */
.form-field:has(input:invalid) {
border-color: red;
}
Real-World Use Case
Style navigation links differently when one is active:
/* Dim other nav items when one is hovered */
nav:has(a:hover) a:not(:hover) {
opacity: 0.5;
}
Container Queries
Style components based on their container size instead of the viewport.
.card-container {
container-type: inline-size;
}
.card {
display: flex;
flex-direction: column;
}
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
Why Container Queries Matter
Components adapt to where they are placed, not just the screen size. A card in a sidebar behaves differently than the same card in the main content area.
Key Takeaways
Modern CSS that you should use today:
- Custom properties for colors, spacing, and theming
- Flexbox for one-dimensional layouts and centering
- Grid for two-dimensional layouts and page structure
:has()for parent selectors (with browser support check)- Container queries for truly reusable components
These features reduce JavaScript complexity and make your CSS more maintainable.