Modern CSS Patterns
CSS has evolved massively. You can build complex layouts and design systems without preprocessors or frameworks.
Custom Properties (CSS Variables)
Define a design system with variables:
:root {
--color-primary: #6366f1;
--color-surface: #1e1e2e;
--color-text: #e2e8f0;
--radius: 0.5rem;
--shadow: 0 4px 6px -1px rgb(0 0 0 / 0.3);
--transition: 200ms ease;
}
.card {
background: var(--color-surface);
color: var(--color-text);
border-radius: var(--radius);
box-shadow: var(--shadow);
transition: transform var(--transition);
}
.card:hover {
transform: translateY(-2px);
}
Grid Layout
CSS Grid handles two-dimensional layouts with minimal code:
/* Responsive card grid — no media queries needed */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
}
/* Dashboard layout */
.dashboard {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr;
grid-template-areas:
"sidebar header"
"sidebar main";
min-height: 100vh;
}
Container Queries
Style based on the container's size, not the viewport:
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
Logical Properties
Write CSS that works in any language direction:
/* Instead of margin-left / margin-right */
.element {
margin-inline: auto; /* Centers horizontally */
padding-block: 1rem; /* Top and bottom */
border-inline-start: 3px solid var(--color-primary);
}
The :has() Selector
Parent selection is finally possible:
/* Style a card differently when it contains an image */
.card:has(img) {
grid-template-rows: 200px 1fr;
}
/* Style a form group when its input is invalid */
.form-group:has(:invalid) {
border-color: red;
}
Dark Mode
Respect user preferences with prefers-color-scheme:
@media (prefers-color-scheme: light) {
:root {
--color-surface: #ffffff;
--color-text: #1e293b;
}
}