Modern CSS Techniques for Better UX
CSS has evolved dramatically in recent years. From container queries to scroll-driven animations, modern CSS offers powerful tools to create exceptional user experiences without relying heavily on JavaScript.
Container Queries: The Game Changer
Container queries revolutionize responsive design by allowing components to adapt based on their container's size, not just the viewport.
Traditional Media Queries vs Container Queries
Old approach:
/* Limited to viewport width */
@media (max-width: 768px) {
.card {
flex-direction: column;
}
}
Modern approach:
/* Responds to container size */
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (max-width: 400px) {
.card {
flex-direction: column;
}
}
Practical Example: Responsive Card Component
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1rem;
container-type: inline-size;
}
.product-card {
display: flex;
background: white;
border-radius: 8px;
overflow: hidden;
}
/* When card container is small, stack vertically */
@container (max-width: 350px) {
.product-card {
flex-direction: column;
}
.product-image {
width: 100%;
height: 200px;
}
}
/* When card container is large, show horizontally */
@container (min-width: 500px) {
.product-card {
flex-direction: row;
}
.product-image {
width: 200px;
height: auto;
}
}
CSS Subgrid: Perfect Alignment
Subgrid allows child grids to participate in their parent's grid, creating perfect alignment across complex layouts.
The Problem Subgrid Solves
<div class="card-grid">
<div class="card">
<h3>Short Title</h3>
<p>Brief description</p>
<button>Action</button>
</div>
<div class="card">
<h3>This is a Much Longer Title That Wraps</h3>
<p>This is a longer description with more content</p>
<button>Action</button>
</div>
</div>
With Subgrid:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1rem;
grid-template-rows: subgrid;
}
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3; /* title, content, button */
}
/* All buttons align perfectly regardless of content length */
.card button {
margin-top: auto;
}
Scroll-Driven Animations
Create engaging animations that respond to scroll position without JavaScript.
Basic Scroll Timeline
@keyframes slide-in {
from {
opacity: 0;
transform: translateY(100px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.scroll-element {
animation: slide-in linear;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
Progress Indicator
.progress-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 100%);
transform-origin: left;
animation: progress linear;
animation-timeline: scroll(root);
}
@keyframes progress {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
Parallax Effects
.parallax-bg {
animation: parallax linear;
animation-timeline: scroll(root);
}
@keyframes parallax {
from {
transform: translateY(0);
}
to {
transform: translateY(-50%);
}
}
Advanced Layout Techniques
CSS Grid Areas for Complex Layouts
.layout {
display: grid;
grid-template-areas:
'header header header'
'sidebar main aside'
'footer footer footer';
grid-template-columns: 250px 1fr 200px;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
gap: 1rem;
}
.header {
grid-area: header;
}
.sidebar {
grid-area: sidebar;
}
.main {
grid-area: main;
}
.aside {
grid-area: aside;
}
.footer {
grid-area: footer;
}
/* Responsive adjustment */
@media (max-width: 768px) {
.layout {
grid-template-areas:
'header'
'main'
'aside'
'sidebar'
'footer';
grid-template-columns: 1fr;
}
}
Flexbox Gap and Wrapping
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.tag {
padding: 0.25rem 0.75rem;
background: #f3f4f6;
border-radius: 9999px;
font-size: 0.875rem;
white-space: nowrap;
}
Modern Color and Typography
CSS Custom Properties for Dynamic Theming
:root {
--primary-h: 220;
--primary-s: 100%;
--primary-l: 50%;
--primary: hsl(var(--primary-h) var(--primary-s) var(--primary-l));
--primary-light: hsl(
var(--primary-h) var(--primary-s) calc(var(--primary-l) + 20%)
);
--primary-dark: hsl(
var(--primary-h) var(--primary-s) calc(var(--primary-l) - 20%)
);
}
/* Dark mode adjustments */
@media (prefers-color-scheme: dark) {
:root {
--primary-l: 70%;
}
}
.button {
background: var(--primary);
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
transition: background 0.2s ease;
}
.button:hover {
background: var(--primary-dark);
}
Fluid Typography
.fluid-text {
font-size: clamp(1rem, 2.5vw, 2rem);
line-height: 1.5;
}
/* More control with CSS locks */
.hero-title {
font-size: calc(1.5rem + 2vw);
max-width: 20ch; /* Optimal reading length */
}
Interactive CSS Features
CSS-Only Accordions
.accordion-item {
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
overflow: hidden;
margin-bottom: 0.5rem;
}
.accordion-trigger {
width: 100%;
padding: 1rem;
background: none;
border: none;
text-align: left;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.accordion-trigger::after {
content: '+';
transition: transform 0.2s ease;
}
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.accordion-item:has(.accordion-trigger:checked) .accordion-content {
max-height: 1000px; /* Large enough value */
}
.accordion-item:has(.accordion-trigger:checked) .accordion-trigger::after {
transform: rotate(45deg);
}
Smooth State Transitions
.card {
background: white;
border-radius: 0.5rem;
padding: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 20px 25px rgba(0, 0, 0, 0.1);
}
/* Loading states */
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
Performance Optimization
CSS Containment
.independent-component {
contain: layout style paint;
}
.list-item {
contain: layout paint;
/* Browser can optimize rendering */
}
Efficient Selectors
/* Good: Specific and efficient */
.product-card .price {
font-weight: bold;
}
/* Avoid: Overly broad selectors */
* {
box-sizing: border-box; /* This is fine for reset */
}
/* Avoid: Deep nesting */
.nav ul li a span {
color: blue; /* Too specific */
}
Critical CSS Inlining
<style>
/* Critical above-the-fold styles */
.header,
.hero,
.nav {
/* Essential styles here */
}
</style>
<link
rel="preload"
href="/styles/main.css"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
/>
Accessibility Enhancements
Focus Management
.button {
outline: none;
position: relative;
}
.button::before {
content: '';
position: absolute;
inset: -2px;
border: 2px solid transparent;
border-radius: inherit;
transition: border-color 0.2s ease;
}
.button:focus-visible::before {
border-color: #3b82f6;
}
/* High contrast mode support */
@media (prefers-contrast: high) {
.button {
border: 2px solid;
}
}
Reduced Motion Support
.animation {
animation: slide-in 0.3s ease;
}
@media (prefers-reduced-motion: reduce) {
.animation {
animation: none;
}
* {
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
}
}
Future CSS Features
CSS Anchor Positioning
.tooltip {
position: absolute;
anchor-name: --trigger;
position-anchor: --trigger;
top: anchor(bottom);
left: anchor(center);
transform: translateX(-50%);
}
CSS Nesting (Now Available)
.card {
background: white;
border-radius: 0.5rem;
& .title {
font-size: 1.25rem;
font-weight: 600;
& span {
color: #6b7280;
}
}
&:hover {
transform: translateY(-2px);
}
}
Best Practices Summary
Organization
/* 1. Custom properties */
:root {
--primary: #3b82f6;
}
/* 2. Base styles */
* {
box-sizing: border-box;
}
/* 3. Layout */
.container {
max-width: 1200px;
margin: 0 auto;
}
/* 4. Components */
.button {
/* ... */
}
/* 5. Utilities */
.sr-only {
/* ... */
}
/* 6. Media queries */
@media (max-width: 768px) {
/* ... */
}
Naming Conventions
/* BEM methodology */
.card {
} /* Block */
.card__title {
} /* Element */
.card--featured {
} /* Modifier */
/* Utility classes */
.text-center {
text-align: center;
}
.mb-4 {
margin-bottom: 1rem;
}
Conclusion
Modern CSS provides powerful tools for creating exceptional user experiences. By embracing container queries, subgrid, scroll-driven animations, and other cutting-edge features, we can build more responsive, performant, and accessible websites.
The key is to use these features progressively, ensuring fallbacks for older browsers while enhancing the experience for users with modern browsers.