2.4 Modern UI Components
Explore your creativity to develop advanced CSS animations and create engaging, modern UI components. In this lesson, we'll discover how to design dynamic user interfaces that combine modern aesthetics with advanced functionality using the latest CSS techniques.
Educational objectives
- Mastering advanced CSS animations with @keyframes and transform properties
- Create modern UI components with sophisticated visual effects
- Implement micro-interactions to improve the user experience
- Use modern CSS techniques like backdrop-filter and clip-path
- Develop responsive interfaces with smooth animations
The fundamentals of modern CSS animations
Advanced CSS animations are at the heart of modern interfaces. They enable the creation of smooth transitions, engaging micro-interactions, and sophisticated visual effects. The `transform`, `@keyframes`, and `animation` properties offer precise control over the movement and transformation of elements. The `transform` property allows for 2D and 3D rotations, translations, scaling, and distortions, while `@keyframes` defines the key frames of a complex animation. Custom timing functions, such as `cubic-bezier()`, allow for the creation of natural and expressive animation curves.
3D transformations and perspective
3D transformations add an extra dimension to web animations. The perspective property creates a vanishing point that simulates depth, while `transform-style: preserve-3d` maintains the 3D transformations of child elements. Rotations around the X, Y, and Z axes (`rotateX()`, `rotateY()`, `rotateZ()`) allow you to create flip, tilt, and rotate effects in space. The `backface-visibility` property controls the visibility of the back face of transformed elements, which is essential for flip-map effects.
Key point : Animation performance is optimal with the transform and opacity properties, which trigger GPU compositing rather than browser repainting.
Key points to remember
Advanced CSS animations combining @keyframes, 3D transform and custom timing-functions create immersive and high-performance visual experiences.
🎯 Mini-exercise: Interactive 3D card flip
Create a map that flips into 3D on hover, revealing content on its back side with smooth animation and realistic perspective.
Reference code:
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
<h3>UI Component</h3>
<p>Modern interface</p>
</div>
<div class="flip-card-back">
<h3>CSS animation</h3>
<p>Advanced 3D Transform</p>
</div>
</div>
</div>
.flip-card { background-color: transparent; width: 300px; height: 200px; perspective: 1000px; margin: 50px auto; } .flip-card-inner { position: relative; width: 100%; height: 100%; text-align: center; transition: transform 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275); transform-style: preserve-3d; } .flip-card:hover .flip-card-inner { transform: rotateY(180deg); } .flip-card-front, .flip-card-back { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; border-radius: 16px; display:flex; flex-direction: column; justify-content: center; align-items: center; box-shadow: 0 10px 30px rgba(0,0,0,0.2); } .flip-card-front { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color:white; } .flip-card-back { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color:white; transform: rotateY(180deg); }
// Optional JavaScript code for advanced controls
Monaco Editor v0.45
Glassmorphism and backdrop-filter
Glassmorphism is a design trend that simulates the effect of frosted glass, creating elegant and modern interfaces. This technique primarily uses `backdrop-filter: blur()` to blur the background, combined with subtle borders and transparencies. The glass effect is achieved by layering a semi-transparent background, a blurred background, and subtle highlights. The `border` and `box-shadow` properties with light values enhance the illusion of depth. This approach works particularly well on colored backgrounds or images, creating a natural visual hierarchy.
Advanced gradients and color animations
Modern CSS gradients go beyond simple linear transitions. Radial and conic gradients, as well as multiple color blends, create sophisticated visual effects. Keyframe-based gradient animation enables smooth and captivating color transitions. The background-size property, combined with variable positioning, creates motion effects on gradients. Masking techniques (mask-image) and mix-blend-mode allow you to combine multiple gradients for complex and original effects.
Key point : backdrop-filter requires a semi-transparent element to reveal the background blur effect. Always provide an alternative for incompatible browsers.
Key points to remember
Glassmorphism and animated gradients create visually rich interfaces that follow contemporary design trends while maintaining excellent readability.
🎯 Mini-exercise: Card glassmorphism with animated gradient
Create a map with a glass effect using backdrop-filter and an animated background gradient that changes colors continuously.
Reference code:
<div class="glass-container">
<div class="glass-card">
<h3>Glassmorphism</h3>
<p>Modern interface with glass effect and animated gradient</p>
<button class="glass-btn">Discover</button>
</div>
</div>
.glass-container { height: 100vh; background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); background-size: 400% 400%; animation: gradientShift 15s ease infinite; display:flex; justify-content: center; align-items: center; padding: 20px; } @keyframes gradientShift { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } .glass-card { background: rgba(255, 255, 255, 0.15); backdrop-filter: blur(20px); border-radius: 20px; border: 1px solid rgba(255, 255, 255, 0.2); padding: 40px; text-align: center; box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37), inset 0 1px 0 0 rgba(255, 255, 255, 0.4); max-width: 400px; color:white; transition: transform 0.3s ease; } .glass-card:hover { transform: translateY(-10px) scale(1.02); } .glass-btn { background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 25px; color:white; padding: 12px 30px; font-size: 16px; cursor: pointer; transition: all 0.3s ease; margin-top: 20px; } .glass-btn:hover { background: rgba(255, 255, 255, 0.3); transform: scale(1.05); }
// JavaScript code for advanced interactions
Monaco Editor v0.45
Design of meaningful micro-interactions
Micro-interactions are the subtle details that transform a functional interface into a memorable experience. They provide immediate feedback to user actions, guide navigation, and create a sense of responsiveness. An effective micro-interaction comprises four phases: trigger (hover, click), rules (what happens), visual feedback (animation, state change), and loops/modes (persistent states). CSS transitions with appropriate timing functions (ease-out for inputs, ease-in for outputs) create natural movement. The strategic use of `transform:scale()`, `rotate()`, and `translate()` for hover and active states enhances the perception of interactivity.
Interface states and fluid transitions
Managing interface states (normal, hover, active, focus, disabled) requires a consistent and predictable approach. Each state should have a distinct visual signature while maintaining the component's identity. Transitions between states should be fast enough not to slow down interaction (typically 100-300ms) but slow enough to be perceptible. The `transition-property` allows precise control over which properties are animated. Using `transform` and `opacity` for animations maintains optimal performance by avoiding browser reflows.
Key point Micro-interactions should never hinder usability. They should be subtle, quick, and optional (respect prefers-reduced-motion).
Key points to remember
Successful micro-interactions combine instant feedback, smooth transitions, and respect for user preferences to create an intuitive and engaging experience.
🎯 Mini-exercise: Interactive button with morphing loader
Create a button that transforms into an animated loader on click, with micro-interactions on hover and distinct visual states.
Reference code:
<button class="morphing-btn" id="morphBtn">
<span class="btn-text">Confirm</span>
<span class="btn-loader">
<div class="loader-dot"></div>
<div class="loader-dot"></div>
<div class="loader-dot"></div>
</span>
<span class="btn-success">✓</span>
</button>
.morphing-btn { position: relative; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color:white; border:none; border-radius: 50px; padding: 16px 40px; font-size: 16px; font-weight: 600; cursor: pointer; overflow:hidden; transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1); min-width: 140px; height: 56px; } .morphing-btn:hover { transform: translateY(-2px); box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4); } .morphing-btn:active { transform: translateY(0); } .btn-text, .btn-loader, .btn-success { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); transition: all 0.4s ease; } .btn-loader, .btn-success { opacity: 0; pointer-events: none; } .btn-loader { display: flex; gap: 6px; } .loader-dot { width: 8px; height: 8px; border-radius: 50%; background:white; animation: loader-bounce 1.4s infinite; } .loader-dot:nth-child(2) { animation-delay: 0.2s; } .loader-dot:nth-child(3) { animation-delay: 0.4s; } @keyframes loader-bounce { 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; } 40% { transform: scale(1.2); opacity: 1; } } .morphing-btn.is-loading { border-radius: 28px; min-width: 56px; padding: 16px; } .morphing-btn.is-loading .btn-text { opacity: 0; transform: translate(-50%, -50%) scale(0.8); } .morphing-btn.is-loading .btn-loader { opacity: 1; } .morphing-btn.is-success { background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); } .morphing-btn.is-success .btn-loader { opacity: 0; } .morphing-btn.is-success .btn-success { opacity: 1; font-size: 24px; }
document.addEventListener('DOMContentLoaded', function() { const btn = document.getElementById('morphBtn'); btn.addEventListener('click', function() { if (!btn.classList.contains('is-loading') && !btn.classList.contains('is-success')) { // Phase loading btn.classList.add('is-loading'); // Simulation of an asynchronous action setTimeout(() => { btn.classList.remove('is-loading'); btn.classList.add('is-success'); // Reset after 2 seconds setTimeout(() => { btn.classList.remove('is-success'); }, 2000); }, 2000); } });
Monaco Editor v0.45
Modular CSS component architecture
Modern UI components require a scalable and maintainable CSS architecture. The Block Element Modifier (BEM) approach, combined with custom CSS properties, allows for the creation of reusable and themable components. CSS variables (`--variable-name`) centralize design values (colors, spacing, border radii) and facilitate theme variations. The use of state classes (`.is-active`, `.is-disabled`, `.is-expanded`) separates the visual logic from the HTML structure. CSS composition techniques such as property inheritance and cascading allow for the creation of component variations without duplicating the base code.
Advanced layouts with CSS Grid and Flexbox
CSS Grid and Flexbox offer sophisticated layout possibilities for modern components. Grid excels in complex two-dimensional layouts, allowing the creation of responsive interfaces with grid-template-areas and dynamic columns/rows. The subgrid property (increasing support) enables perfect alignment of nested elements. Flexbox remains ideal for one-dimensional components and precise element alignment. Advanced techniques include the use of gap for spacing, aspect ratio for proportions, and the min(), max(), and clamp() functions for sophisticated responsive dimensions.
Key point : Custom CSS properties inherit in a cascading manner, allowing the creation of themes and variations of components with a declarative and efficient approach.
Key points to remember
Modern CSS architecture combines custom properties, advanced layouts, and naming methodologies to create maintainable and scalable components.
🎯 Mini-exercise: Dashboard layout with Grid and themable components
Create a responsive dashboard layout using CSS Grid with themable cards via custom properties and sequential entry animations.
Reference code:
<div class="dashboard" data-theme="default">
<header class="dashboard-header">
<h1>Dashboard UI</h1>
<div class="theme-switcher">
<button data-theme="default">Default</button>
<button data-theme="dark">Dark</button>
<button data-theme="colorful">Colorful</button>
</div>
</header>
<div class="dashboard-grid">
<div class="dashboard-card" style="--delay: 0s">
<h3>Analytics</h3>
<div class="card-metric">1,234</div>
<p>Total Users</p>
</div>
<div class="dashboard-card" style="--delay: 0.1s">
<h3>Revenue</h3>
<div class="card-metric">€45,678</div>
<p>This Month</p>
</div>
<div class="dashboard-card dashboard-card--wide" style="--delay: 0.2s">
<h3>Performance</h3>
<div class="performance-bars">
<div class="perf-bar" style="--width: 75%"></div>
<div class="perf-bar" style="--width: 60%"></div>
<div class="perf-bar" style="--width: 90%"></div>
</div>
</div>
<div class="dashboard-card" style="--delay: 0.3s">
<h3>Status</h3>
<div class="status-indicator"></div>
<p>System Online</p>
</div>
</div>
</div>
:root { --primary-color: #667eea; --secondary-color: #764ba2; --background: #f8fafc; --card-background: white; --text-primary: #1a202c; --text-secondary: #4a5568; --border-color: #e2e8f0; --shadow: 0 4px 6px rgba(0, 0, 0, 0.05); } [data-theme="dark"] { --primary-color: #9f7aea; --secondary-color: #667eea; --background: #1a202c; --card-background: #2d3748; --text-primary: #f7fafc; --text-secondary: #cbd5e0; --border-color: #4a5568; --shadow: 0 4px 6px rgba(0, 0, 0, 0.3); } [data-theme="colorful"] { --primary-color: #ff6b6b; --secondary-color: #4ecdc4; --background: #ffe66d; --card-background: white; --text-primary: #2c3e50; --text-secondary: #7f8c8d; --border-color: #f39c12; --shadow: 0 8px 25px rgba(255, 107, 107, 0.15); } .dashboard { background: var(--background); min-height: 100vh; padding: 20px; color: var(--text-primary); transition: all 0.4s ease; } .dashboard-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 30px; } .theme-switcher button { background: var(--card-background); color: var(--text-primary); border: 1px solid var(--border-color); padding: 8px 16px; margin-left: 8px; border-radius: 6px; cursor: pointer; transition: all 0.2s ease; } .theme-switcher button:hover { background: var(--primary-color); color:white; } .dashboard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; max-width: 1200px; } .dashboard-card { background: var(--card-background); border-radius: 12px; padding: 24px; box-shadow: var(--shadow); border: 1px solid var(--border-color); transition: all 0.3s ease; animation: cardEnter 0.6s ease forwards; animation-delay: var(--delay); opacity: 0; transform: translateY(20px); } @keyframes cardEnter { to { opacity: 1; transform: translateY(0); } } .dashboard-card:hover { transform: translateY(-4px); box-shadow: 0 12px 25px rgba(0, 0, 0, 0.1); } .dashboard-card--wide { grid-column: span 2; } .card-metric { font-size: 2.5rem; font-weight: bold; color: var(--primary-color); margin: 12px 0; } .performance-bars { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; } .perf-bar { height: 8px; background: var(--border-color); border-radius: 4px; position: relative; overflow:hidden; } .perf-bar::before { content: ''; position: absolute; left: 0; top: 0; height: 100%; width: var(--width); background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); border-radius: 4px; animation: barFill 1s ease 0.5s forwards; transform: scaleX(0); transform-origin:left; } @keyframes barFill { to { transform: scaleX(1); } } .status-indicator { width: 16px; height: 16px; border-radius: 50%; background: #48bb78; animation: pulse 2s infinite; margin: 12px 0; } @keyframes pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(72, 187, 120, 0.4); } 50% { box-shadow: 0 0 0 10px rgba(72, 187, 120, 0); } }
document.addEventListener('DOMContentLoaded', function() { const dashboard = document.querySelector('.dashboard'); const themeButtons = document.querySelectorAll('[data-theme]'); themeButtons.forEach(button => { button.addEventListener('click', () => { const theme = button.getAttribute('data-theme'); dashboard.setAttribute('data-theme', theme); // Reanimate cards const cards = document.querySelectorAll('.dashboard-card'); cards.forEach((card, index) => { card.style.animation = 'none'; card.offsetHeight; // Force reflow card.style.animation = `cardEnter 0.4s ease forwards`; card.style.animationDelay = `${index * 0.05}s`; }); }); });
Monaco Editor v0.45
CSS animation performance
Optimizing CSS animations is crucial for maintaining a smooth 60fps interface. The `transform` and `opacity` properties are the only ones that trigger only the composition, avoiding costly repainting. Using `will-change` informs the browser of upcoming animations, allowing for prior GPU optimization. However, this property should be used sparingly and removed after the animation. Optimization techniques include reducing the number of elements animated simultaneously, using `transform3d()` to force hardware acceleration, and limiting complex animations on lower-powered devices via performance media queries.
Accessibility and respect for user preferences
Accessibility for animations requires special attention to motion-sensitive users. The media query `prefers-reduced-motion` detects this system preference and adapts animations accordingly. Alternatives include reducing duration, eliminating repetitive movements, or transforming animations into instantaneous transitions. Keyboard accessibility must be preserved: animated interactive elements must remain focusable, and their focus states must be clearly visible. Animations should never obscure critical information or prevent normal interaction with the interface.
Key point : Always test animations on less powerful devices and implement prefers-reduced-motion for an inclusive experience.
Key points to remember
High-performance and accessible animations use optimized properties, respect user preferences, and maintain usability across all devices.
🎯 Mini-exercise: Preference-respecting animation system
Create a component with animations that automatically adapts to movement preferences and includes accessible alternatives.
Reference code:
<div class="accessible-demo">
<h2>Animation Accessibility Demo</h2>
<div class="preference-info">
<p>Your motion preference: <span id="motionPreference">Detecting...</span></p>
<button id="toggleMotion">Toggle Animation Mode</button>
</div>
<div class="animated-cards">
<div class="anim-card" tabindex="0">
<h3>Performance</h3>
<p>Optimized animations using transform and opacity</p>
<div class="card-icon">⚡</div>
</div>
<div class="anim-card" tabindex="0">
<h3>Accessibility</h3>
<p>Respects prefers-reduced-motion settings</p>
<div class="card-icon">♿</div>
</div>
<div class="anim-card" tabindex="0">
<h3>Responsive</h3>
<p>Adapts to different devices and preferences</p>
<div class="card-icon">📱</div>
</div>
</div>
</div>
/* Variables for animations */ :root { --animation-duration: 0.3s; --animation-scale: 1.05; --animation-translate: -8px; } /* Reduced mode for prefers-reduced-motion */ @media (prefers-reduced-motion: reduce) { :root { --animation-duration: 0.1s; --animation-scale: 1.02; --animation-translate: -2px; } * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } .accessible-demo { max-width: 1000px; margin: 0 auto; padding: 20px; font-family: 'Segoe UI', sans-serif; } .preference-info { background: #f0f4f8; padding: 20px; border-radius: 8px; margin-bottom: 30px; text-align: center; } #motionPreference { font-weight: bold; color: #667eea; } #toggleMotion { background: #667eea; color:white; border:none; padding: 10px 20px; border-radius: 6px; cursor: pointer; margin-top: 10px; transition: background-color var(--animation-duration) ease; } #toggleMotion:hover { background: #5a67d8; } #toggleMotion:focus { outline: 2px solid #667eea; outline-offset: 2px; } .animated-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; } .anim-card { background: white; border-radius: 12px; padding: 24px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); border: 2px solid transparent; transition: all var(--animation-duration) cubic-bezier(0.4, 0, 0.2, 1); position: relative; cursor: pointer; /* GPU Optimization */ will-change: transform; } .anim-card:hover { transform: translateY(var(--animation-translate)) scale(var(--animation-scale)); box-shadow: 0 12px 25px rgba(0, 0, 0, 0.15); } .anim-card:focus { outline: none; border-color: #667eea; transform: translateY(var(--animation-translate)) scale(var(--animation-scale)); } .card-icon { position: absolute; top: 20px; right: 20px; font-size: 24px; opacity: 0.7; transition: all var(--animation-duration) ease; } .anim-card:hover .card-icon, .anim-card:focus .card-icon { opacity: 1; transform: scale(1.2); } /* Custom reduced animation mode */ .reduce-motion .anim-card { --animation-duration: 0.1s; --animation-scale: 1.01; --animation-translate: -1px; } .reduce-motion .anim-card:hover, .reduce-motion .anim-card:focus { background: #f7fafc; } /* Input animations with staggering */ .anim-card { animation: cardFadeIn 0.6s ease forwards; opacity: 0; transform: translateY(20px); } .anim-card:nth-child(1) { animation-delay: 0.1s; } .anim-card:nth-child(2) { animation-delay: 0.2s; } .anim-card:nth-child(3) { animation-delay: 0.3s; } @keyframes cardFadeIn { to { opacity: 1; transform: translateY(0); } } /* Removing reduced mode animations */ @media (prefers-reduced-motion: reduce) { .anim-card { animation: none; opacity: 1; transform:none; } } /* Visual indicator for animations */ .animation-indicator { position: fixed; top: 10px; right: 10px; padding: 8px 12px; background: rgba(0, 0, 0, 0.8); color:white; border-radius: 4px; font-size: 12px; z-index: 1000; }
document.addEventListener('DOMContentLoaded', function() {
const motionPreferenceSpan = document.getElementById('motionPreference');
const toggleMotionBtn = document.getElementById('toggleMotion');
const demoContainer = document.querySelector('.accessible-demo');
// Détecter la préférence de mouvement
function checkMotionPreference() {
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
return prefersReducedMotion;
}
// Mettre à jour l'affichage des préférences
function updatePreferenceDisplay() {
const isReduced = checkMotionPreference() || demoContainer.classList.contains('reduce-motion');
motionPreferenceSpan.textContent = isReduced ? 'Reduced Motion' : 'Full Animation';
motionPreferenceSpan.style.color = isReduced ? '#e53e3e' : '#38a169';
}
// Toggle manuel du mode animation
toggleMotionBtn.addEventListener('click', function() {
demoContainer.classList.toggle('reduce-motion');
updatePreferenceDisplay();
// Feedback visuel
this.textContent = demoContainer.classList.contains('reduce-motion') ?
'Enable Full Animations' : 'Reduce Animations';
});
// Écouter les changements de préférence système
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
mediaQuery.addEventListener('change', updatePreferenceDisplay);
// Gestion de l'accessibilité clavier
const cards = document.querySelectorAll('.anim-card');
cards.forEach(card => {
card.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.click();
}
});
card.addEventListener('click', function() {
// Animation de feedback au clic
this.style.transform = 'scale(0.95)';
setTimeout(() => {
this.style.transform = '';
}, 150);
});
});
// Optimisation will-change
cards.forEach(card => {
card.addEventListener('mouseenter', () => {
card.style.willChange = 'transform, box-shadow';
});
card.addEventListener('mouseleave', () => {
card.style.willChange = 'auto';
});
});
// Initialisation
updatePreferenceDisplay();
// Créer un indicateur d'animation
const indicator = document.createElement('div');
indicator.className = 'animation-indicator';
indicator.textContent = 'GPU Accelerated';
document.body.appendChild(indicator);
});
Monaco Editor v0.45
Summary
Key points to remember
- Advanced CSS Animations The transform and @keyframes properties allow you to create sophisticated visual effects with optimal performance thanks to GPU acceleration.
- Modern visual effects Glassmorphism with backdrop filters and animated gradients creates contemporary and visually appealing interfaces.
- Micro-interactions Subtle transitions and visual feedback enhance the user experience by creating responsive and intuitive interfaces.
- Modular architecture Custom CSS properties and naming conventions allow for the creation of reusable and themable components.
- Accessibility and performance Respecting prefers-reduced-motion and optimizing animations ensures an inclusive experience on all devices.
Sources
To deepen your knowledge:
Validate your knowledge
Test your understanding with this 10-question quiz:
[
Validate your knowledge
Test your understanding with this 10-question quiz:

