CSS Filters: blur, brightness, contrast & More
Introduction to CSS Filters
CSS filters give you the power to apply visual effects to elements directly in the browser -- effects that previously required image editing software like Photoshop. With the filter property, you can blur elements, adjust their brightness and contrast, convert them to grayscale, apply sepia tones, rotate hues, and much more. These effects are applied at render time, meaning they work on any element: images, text, backgrounds, videos, and even entire sections of a page.
Filters are not just decorative tricks. They are essential tools for creating polished user interfaces. You can dim background content when a modal is open, create hover effects on image galleries, build frosted glass navigation bars, implement dark mode adjustments, and design accessible focus indicators. Because filters are GPU-accelerated in most browsers, they perform well even on complex layouts when used thoughtfully.
The filter property accepts one or more filter functions. Each function takes specific parameters that control the intensity of the effect. You can chain multiple filter functions together in a single declaration, and the browser applies them in the order you specify -- left to right. This composability is what makes CSS filters truly powerful.
Basic Filter Syntax
/* Single filter */
.element {
filter: blur(5px);
}
/* Multiple filters chained together */
.element {
filter: blur(2px) brightness(1.2) contrast(1.1);
}
/* Remove all filters */
.element {
filter: none;
}
opacity or transform. This means a filtered element establishes its own z-index context, which can affect how it layers with sibling elements. Keep this in mind when combining filters with complex z-index layouts.The blur() Filter
The blur() function applies a Gaussian blur to the element. It accepts a single length value that defines the blur radius -- larger values produce a more pronounced blur. A value of 0 leaves the element unblurred. The blur extends beyond the element's boundaries, so neighboring content may appear to bleed into the blurred area unless you use overflow: hidden on a parent container.
Using blur() with Different Values
/* Subtle blur -- like slightly out of focus */
.subtle-blur {
filter: blur(1px);
}
/* Medium blur -- clearly out of focus */
.medium-blur {
filter: blur(4px);
}
/* Heavy blur -- content is unreadable */
.heavy-blur {
filter: blur(10px);
}
/* Blur a background image behind text */
.hero-background {
filter: blur(8px);
transform: scale(1.1); /* Prevent white edges from blur overflow */
}
/* Blur sensitive content until user interaction */
.spoiler {
filter: blur(8px);
cursor: pointer;
transition: filter 0.3s ease;
}
.spoiler:hover,
.spoiler:focus {
filter: blur(0);
}
transform: scale(1.1) or slightly increase the element's size so the blurred edges extend beyond the visible area. Then use overflow: hidden on the parent to clip them.The brightness() Filter
The brightness() function adjusts how bright or dark an element appears. It accepts a number or percentage as its parameter. A value of 1 (or 100%) leaves the element unchanged. Values below 1 darken the element (with 0 producing a completely black result), and values above 1 brighten it, potentially washing out colors at very high values.
Brightness Adjustments
/* Completely black */
.blackout {
filter: brightness(0);
}
/* 50% darker than original */
.dimmed {
filter: brightness(0.5);
}
/* Original brightness (no change) */
.normal {
filter: brightness(1);
}
/* 30% brighter than original */
.brightened {
filter: brightness(1.3);
}
/* Double brightness -- washed out look */
.overexposed {
filter: brightness(2);
}
/* Darken images on hover for text overlay readability */
.card-image {
transition: filter 0.3s ease;
}
.card:hover .card-image {
filter: brightness(0.6);
}
The contrast() Filter
The contrast() function increases or decreases the contrast of an element. Contrast is the difference between the lightest and darkest parts of an image. A value of 1 (or 100%) is unchanged. Values below 1 reduce contrast, making the element look washed out and gray. Values above 1 increase contrast, making darks darker and lights lighter.
Contrast Adjustments
/* No contrast -- completely gray */
.no-contrast {
filter: contrast(0);
}
/* Low contrast -- muted, flat look */
.low-contrast {
filter: contrast(0.5);
}
/* Original contrast */
.normal {
filter: contrast(1);
}
/* High contrast -- punchier colors */
.high-contrast {
filter: contrast(1.5);
}
/* Extreme contrast -- almost posterized */
.extreme-contrast {
filter: contrast(3);
}
/* Combine brightness and contrast for photo enhancement */
.enhanced-photo {
filter: brightness(1.1) contrast(1.2);
}
The grayscale() Filter
The grayscale() function converts an element to grayscale. It accepts a value from 0 to 1 (or 0% to 100%). A value of 0 leaves the element in full color, while 1 converts it to complete grayscale. Intermediate values produce a partial desaturation effect, which can create subtle, muted color palettes.
Grayscale Conversions
/* Full color (no change) */
.full-color {
filter: grayscale(0);
}
/* Partially desaturated -- muted colors */
.muted {
filter: grayscale(0.5);
}
/* Completely grayscale -- black and white */
.black-and-white {
filter: grayscale(1);
}
/* Common pattern: grayscale that reveals color on hover */
.partner-logo {
filter: grayscale(1);
opacity: 0.7;
transition: filter 0.3s ease, opacity 0.3s ease;
}
.partner-logo:hover {
filter: grayscale(0);
opacity: 1;
}
/* Grayscale disabled elements */
.disabled-section {
filter: grayscale(1);
pointer-events: none;
opacity: 0.6;
}
The sepia() Filter
The sepia() function applies a warm, brownish tone to an element, reminiscent of old photographs. It accepts a value from 0 to 1 (or 0% to 100%). A value of 0 is unchanged, and 1 applies a full sepia tone. This filter is popular for creating vintage or nostalgic visual effects.
Sepia Tone Effects
/* Subtle warm tone */
.warm {
filter: sepia(0.2);
}
/* Strong vintage look */
.vintage {
filter: sepia(0.8);
}
/* Full sepia */
.old-photograph {
filter: sepia(1);
}
/* Combine sepia with other filters for a complete vintage effect */
.vintage-photo {
filter: sepia(0.6) contrast(1.1) brightness(0.9);
}
The saturate() Filter
The saturate() function adjusts the color saturation of an element. A value of 1 (or 100%) leaves colors unchanged. Values below 1 desaturate the colors (with 0 producing a grayscale result identical to grayscale(1)). Values above 1 super-saturate colors, making them more vivid and intense.
Saturation Adjustments
/* Fully desaturated (same as grayscale) */
.desaturated {
filter: saturate(0);
}
/* Slightly muted colors */
.muted-colors {
filter: saturate(0.7);
}
/* Original saturation */
.normal {
filter: saturate(1);
}
/* Vivid, punchy colors */
.vivid {
filter: saturate(1.5);
}
/* Extremely saturated -- neon-like effect */
.hyper-saturated {
filter: saturate(3);
}
/* Make a photo pop with increased saturation */
.instagram-vivid {
filter: saturate(1.4) contrast(1.1) brightness(1.05);
}
The hue-rotate() Filter
The hue-rotate() function shifts all the colors in an element around the color wheel. It accepts an angle value (typically in degrees). A value of 0deg leaves colors unchanged, 180deg shifts all colors to their complementary colors, and 360deg returns to the original colors. This filter is powerful for creating color variations from a single source image or for dynamic color theming.
Hue Rotation Examples
/* No rotation (original colors) */
.original {
filter: hue-rotate(0deg);
}
/* Slight warm shift */
.warm-shift {
filter: hue-rotate(30deg);
}
/* Shift to complementary colors */
.complementary {
filter: hue-rotate(180deg);
}
/* Full rotation (back to original) */
.full-rotation {
filter: hue-rotate(360deg);
}
/* Generate color variations of the same icon */
.icon-red { filter: hue-rotate(0deg); }
.icon-orange { filter: hue-rotate(30deg); }
.icon-green { filter: hue-rotate(120deg); }
.icon-blue { filter: hue-rotate(240deg); }
/* Animated rainbow effect */
@keyframes rainbow {
from { filter: hue-rotate(0deg); }
to { filter: hue-rotate(360deg); }
}
.rainbow-text {
animation: rainbow 5s linear infinite;
}
The invert() Filter
The invert() function inverts the colors of an element. A value of 0 leaves the element unchanged, and 1 (or 100%) completely inverts all colors -- white becomes black, red becomes cyan, and so on. A value of 0.5 produces a neutral gray result because you are exactly halfway between the original and inverted colors.
Color Inversion
/* No inversion */
.normal {
filter: invert(0);
}
/* Full inversion */
.inverted {
filter: invert(1);
}
/* Partial inversion -- muted, unusual colors */
.partial-invert {
filter: invert(0.3);
}
/* Quick dark mode trick for simple pages */
.dark-mode {
filter: invert(1) hue-rotate(180deg);
}
/* Invert dark icons for use on dark backgrounds */
.icon-on-dark {
filter: invert(1);
}
filter: invert(1) hue-rotate(180deg) as a quick dark mode approach will also invert images and videos. You will need to apply the same filter again on <img> and <video> elements to invert them back to their original appearance. This technique is a hack and should not replace a properly designed dark mode with CSS custom properties.The opacity() Filter
The opacity() filter function adjusts the transparency of an element. It works similarly to the CSS opacity property, accepting a value from 0 (fully transparent) to 1 (fully opaque). The key difference is that the opacity() filter can be combined with other filter functions in a single declaration, whereas the opacity property is a standalone property.
opacity() Filter vs opacity Property
/* Using the opacity filter function */
.filter-opacity {
filter: opacity(0.5);
}
/* Using the opacity property */
.property-opacity {
opacity: 0.5;
}
/* The key advantage: combining with other filters */
.combined {
filter: blur(2px) opacity(0.7) grayscale(0.3);
}
/* Hover effect with combined filter transition */
.image-card {
filter: opacity(0.8) grayscale(0.5);
transition: filter 0.3s ease;
}
.image-card:hover {
filter: opacity(1) grayscale(0);
}
opacity property more efficiently than the opacity() filter function. If you only need to change transparency and are not combining it with other filters, prefer the opacity property for better performance.The drop-shadow() Filter
The drop-shadow() filter creates a shadow that follows the actual shape of the element, including any transparent areas. This is fundamentally different from box-shadow, which always creates a rectangular shadow matching the element's bounding box. For images with transparency (like PNGs or SVGs), drop-shadow() traces the contours of the visible pixels, producing a shadow that matches the shape of the content.
drop-shadow() vs box-shadow
/* box-shadow: always a rectangle around the element's box */
.box-shadow-example {
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.3);
}
/* drop-shadow: follows the actual shape of the element */
.drop-shadow-example {
filter: drop-shadow(4px 4px 10px rgba(0, 0, 0, 0.3));
}
/* Syntax: drop-shadow(offset-x offset-y blur-radius color) */
/* Shadow on a transparent PNG image */
.transparent-image {
filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.4));
}
/* Shadow on an SVG icon */
.svg-icon {
filter: drop-shadow(1px 2px 3px rgba(0, 0, 0, 0.3));
}
/* Shadow on a clipped element */
.triangle {
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
}
/* Note: drop-shadow follows the clip-path shape! */
box-shadow, the drop-shadow() filter does not support the inset keyword or the spread parameter. If you need an inset shadow or precise spread control, you must use box-shadow. Use drop-shadow() specifically when you need a shadow that follows irregular shapes, transparent images, or clipped elements.Combining Multiple Filters
One of the most powerful aspects of CSS filters is the ability to chain multiple filter functions together. When you specify multiple filters, the browser applies them sequentially from left to right. The output of the first filter becomes the input for the second filter, and so on. The order of filters matters and can produce different results.
Filter Chaining and Order
/* Order matters! These produce different results: */
/* Blur first, then brighten the blurred result */
.blur-then-bright {
filter: blur(3px) brightness(1.5);
}
/* Brighten first, then blur the brightened result */
.bright-then-blur {
filter: brightness(1.5) blur(3px);
}
/* Instagram-like filter presets */
.filter-warm {
filter: sepia(0.3) saturate(1.4) brightness(1.1) contrast(1.1);
}
.filter-cool {
filter: saturate(0.8) brightness(1.1) hue-rotate(190deg) contrast(1.1);
}
.filter-dramatic {
filter: contrast(1.4) brightness(0.9) saturate(1.3);
}
.filter-faded {
filter: contrast(0.8) brightness(1.1) saturate(0.7) sepia(0.1);
}
.filter-noir {
filter: grayscale(1) contrast(1.4) brightness(0.9);
}
The backdrop-filter Property
While the filter property applies effects to an element and all of its contents, the backdrop-filter property applies effects only to the area behind an element. This creates the popular frosted glass (or glassmorphism) effect where the content behind a semi-transparent element is blurred while the element's own content remains crisp and readable. The element must have some level of transparency (via background-color with an alpha channel or opacity) for the backdrop effect to be visible.
Frosted Glass Effect with backdrop-filter
/* Basic frosted glass card */
.glass-card {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px); /* Safari support */
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 12px;
padding: 24px;
}
/* Frosted glass navigation bar */
.glass-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px) saturate(1.8);
-webkit-backdrop-filter: blur(20px) saturate(1.8);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
padding: 12px 24px;
z-index: 1000;
}
/* Dark frosted glass */
.dark-glass {
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(15px) brightness(0.8);
-webkit-backdrop-filter: blur(15px) brightness(0.8);
color: white;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
}
/* Modal overlay with blurred background */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
}
backdrop-filter property requires the -webkit- prefix for Safari support. Always include both the prefixed and unprefixed versions. Also note that backdrop-filter has a performance cost because the browser must render the content behind the element and then apply the filter effect in real time. Use it sparingly on mobile devices and avoid animating the blur radius itself.backdrop-filter Accepts All Filter Functions
The backdrop-filter property accepts the same filter functions as filter. You can blur, adjust brightness, add contrast, apply grayscale, and more -- all to the background content behind an element.
Various backdrop-filter Effects
/* Blur the background */
.backdrop-blur {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
/* Brighten the background */
.backdrop-bright {
backdrop-filter: brightness(1.5);
-webkit-backdrop-filter: brightness(1.5);
}
/* Grayscale the background */
.backdrop-gray {
backdrop-filter: grayscale(1);
-webkit-backdrop-filter: grayscale(1);
}
/* Combined: blur + saturate (Apple-style glass) */
.apple-glass {
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(20px) saturate(1.8);
-webkit-backdrop-filter: blur(20px) saturate(1.8);
}
/* Combined: blur + brightness for dark overlays */
.dark-overlay {
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(12px) brightness(0.7);
-webkit-backdrop-filter: blur(12px) brightness(0.7);
}
Filter Transitions and Animations
CSS filters are fully animatable, which means you can smoothly transition between different filter states using transition or animate them with @keyframes. This opens up a wide range of interactive effects, from hover reveals to loading animations to scroll-triggered visual changes.
Transitioning Filters on Hover
/* Grayscale to color on hover */
.portfolio-image {
filter: grayscale(1) brightness(0.8);
transition: filter 0.4s ease;
}
.portfolio-image:hover {
filter: grayscale(0) brightness(1);
}
/* Blur reveal on hover */
.blurred-preview {
filter: blur(5px) brightness(0.9);
transition: filter 0.3s ease;
}
.blurred-preview:hover {
filter: blur(0) brightness(1);
}
/* Multiple filter transition */
.photo-card {
filter: saturate(0.6) contrast(0.9) brightness(0.95);
transition: filter 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.photo-card:hover {
filter: saturate(1.2) contrast(1.05) brightness(1.05);
}
Keyframe Animations with Filters
/* Pulsing brightness animation */
@keyframes glowPulse {
0%, 100% {
filter: brightness(1) drop-shadow(0 0 5px rgba(66, 133, 244, 0.3));
}
50% {
filter: brightness(1.2) drop-shadow(0 0 20px rgba(66, 133, 244, 0.6));
}
}
.glowing-element {
animation: glowPulse 2s ease-in-out infinite;
}
/* Blur in/out animation */
@keyframes blurIn {
from {
filter: blur(20px);
opacity: 0;
}
to {
filter: blur(0);
opacity: 1;
}
}
.blur-entrance {
animation: blurIn 0.8s ease-out both;
}
/* Color cycle using hue-rotate */
@keyframes colorCycle {
from { filter: hue-rotate(0deg); }
to { filter: hue-rotate(360deg); }
}
.shifting-colors {
animation: colorCycle 8s linear infinite;
}
/* Loading effect: brightness wave */
@keyframes brightnessWave {
0%, 100% { filter: brightness(0.6); }
50% { filter: brightness(1.2); }
}
.loading-placeholder {
animation: brightnessWave 1.5s ease-in-out infinite;
}
Performance Considerations
Filters are rendered on the GPU in most modern browsers, which generally makes them performant. However, certain scenarios can cause performance issues, especially on mobile devices or when filters are applied to large areas of the screen.
Performance Best Practices
/* GOOD: Filter on a small, fixed-size element */
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
filter: grayscale(1);
transition: filter 0.3s ease;
}
.avatar:hover {
filter: grayscale(0);
}
/* CAUTION: Filter on a large full-screen element */
.full-screen-blur {
filter: blur(20px);
/* This can be expensive because the browser must blur
every pixel of the full viewport on every frame */
}
/* BETTER: Use will-change to hint GPU acceleration */
.animated-filter {
will-change: filter;
transition: filter 0.3s ease;
}
/* CLEAN UP: Remove will-change when not needed */
.animated-filter.idle {
will-change: auto;
}
/* AVOID: Animating backdrop-filter blur radius */
.bad-performance {
/* Do not animate the blur value itself */
transition: backdrop-filter 0.3s ease; /* Expensive! */
}
/* BETTER: Toggle backdrop-filter with opacity */
.overlay {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
opacity: 0;
transition: opacity 0.3s ease;
}
.overlay.visible {
opacity: 1;
}
grayscale(1) to grayscale(0)) rather than animating continuous values (like a steadily increasing blur radius). Toggling between fixed states is much cheaper than continuously recalculating filter values. Also, avoid applying blur() or backdrop-filter to elements that cover large portions of the viewport, especially on mobile.Practical Example: Image Hover Effects
One of the most common uses of CSS filters is creating sophisticated image hover effects for galleries, portfolios, and product grids. By combining filters with transitions, you can create professional-looking interactive effects with pure CSS.
Gallery Image Hover Effects
/* Effect 1: Grayscale to color reveal */
.gallery-item img {
filter: grayscale(1);
transition: filter 0.4s ease, transform 0.4s ease;
}
.gallery-item:hover img {
filter: grayscale(0);
transform: scale(1.05);
}
/* Effect 2: Brighten with shadow on hover */
.gallery-item-v2 img {
filter: brightness(0.85);
transition: filter 0.3s ease;
}
.gallery-item-v2:hover img {
filter: brightness(1.1) drop-shadow(0 10px 20px rgba(0, 0, 0, 0.3));
}
/* Effect 3: Vintage on hover */
.gallery-item-v3 img {
transition: filter 0.5s ease;
}
.gallery-item-v3:hover img {
filter: sepia(0.4) contrast(1.2) brightness(0.9) saturate(1.3);
}
/* Effect 4: Focus effect -- blur siblings */
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.gallery:hover .gallery-item img {
filter: blur(2px) brightness(0.7);
transition: filter 0.3s ease;
}
.gallery:hover .gallery-item:hover img {
filter: blur(0) brightness(1);
}
Practical Example: Frosted Glass Cards
Glassmorphism is a popular design trend that uses backdrop-filter to create translucent, frosted glass UI elements. Here is a complete implementation of frosted glass cards that work over any background.
Complete Frosted Glass Card System
/* Container with a colorful or image background */
.glass-container {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 40px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 24px;
}
/* The glass card itself */
.glass-card {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(12px) saturate(1.5);
-webkit-backdrop-filter: blur(12px) saturate(1.5);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.25);
padding: 32px;
color: white;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.glass-card:hover {
transform: translateY(-4px);
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.15);
}
.glass-card h3 {
margin: 0 0 12px;
font-size: 1.25rem;
}
.glass-card p {
margin: 0;
opacity: 0.85;
line-height: 1.6;
}
/* Glass card variant: dark glass */
.glass-card-dark {
background: rgba(0, 0, 0, 0.25);
backdrop-filter: blur(16px) brightness(0.9);
-webkit-backdrop-filter: blur(16px) brightness(0.9);
border: 1px solid rgba(255, 255, 255, 0.1);
}
Practical Example: Dark Mode with Filters
CSS filters can be used as a quick approach for implementing dark mode on pages or sections where a full CSS custom properties refactor is not practical. While not a replacement for a properly built dark mode, filters can be useful as a fallback or quick prototype technique.
Quick Dark Mode with Filters
/* Apply invert + hue-rotate to create a dark mode effect */
.dark-mode-wrapper {
filter: invert(1) hue-rotate(180deg);
}
/* Revert images and videos back to their original colors */
.dark-mode-wrapper img,
.dark-mode-wrapper video,
.dark-mode-wrapper svg,
.dark-mode-wrapper [data-no-invert] {
filter: invert(1) hue-rotate(180deg);
}
/* Softer dark mode: reduce brightness instead of full invert */
.soft-dark {
filter: brightness(0.8) contrast(1.2);
background-color: #1a1a2e;
}
/* Apply dark mode based on system preference */
@media (prefers-color-scheme: dark) {
.auto-dark {
filter: invert(0.9) hue-rotate(180deg);
}
.auto-dark img,
.auto-dark video {
filter: invert(1) hue-rotate(180deg);
}
}
The url() Filter Function
The url() filter function references an SVG filter element by its ID. This allows you to apply complex SVG filter effects -- such as turbulence, color matrix transformations, and displacement maps -- using the CSS filter property. SVG filters are far more powerful than the built-in CSS filter functions and can create effects that would otherwise be impossible in pure CSS.
Using SVG Filters with CSS
<!-- Define an SVG filter in your HTML -->
<svg style="display: none;">
<defs>
<filter id="noise">
<feTurbulence type="fractalNoise"
baseFrequency="0.65" numOctaves="3"
stitchTiles="stitch" />
<feColorMatrix type="saturate" values="0" />
</filter>
</defs>
</svg>
/* Reference the SVG filter from CSS */
.noise-texture {
filter: url(#noise);
}
/* Combine SVG filter with CSS filter functions */
.combined-effect {
filter: url(#noise) blur(1px) contrast(1.5);
}
Accessibility Considerations
When using CSS filters, always consider their impact on accessibility. Filters that reduce contrast, blur content, or change colors can make content harder to read for users with visual impairments.
Accessible Filter Usage
/* Ensure sufficient contrast when dimming content */
.dimmed-background {
filter: brightness(0.4);
}
.dimmed-background .overlay-text {
/* Use high contrast text on dimmed backgrounds */
color: white;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
/* Respect reduced motion preferences */
@media (prefers-reduced-motion: reduce) {
.animated-filter {
animation: none;
transition: none;
}
}
/* Provide focus indicators that are not affected by filters */
.filtered-element:focus-visible {
outline: 3px solid #4A90D9;
outline-offset: 2px;
/* outline is not affected by the element's filter */
}
/* High contrast mode adjustments */
@media (prefers-contrast: high) {
.muted-image {
filter: none; /* Remove filters that reduce contrast */
}
}
Exercise 1: Interactive Image Gallery
Build an image gallery with 6 images arranged in a 3-column grid with a 16px gap. By default, all images should be displayed with grayscale(1) and brightness(0.8) filters. When the user hovers over any image, that image should transition to full color (grayscale(0)) and slight brightness boost (brightness(1.05)) over 0.4 seconds with an ease timing function. The image should also scale up to 1.05 using transform. Additionally, when any image in the gallery is hovered, all other images should become slightly more blurred (blur(2px)) to create a spotlight effect on the hovered image. Add a drop-shadow filter on hover to make the focused image pop off the page. Include a prefers-reduced-motion media query that removes the scale transform and shortens all transition durations to 0.1 seconds.
Exercise 2: Frosted Glass Dashboard
Create a dashboard layout with a full-viewport background image. On top of the image, build three frosted glass cards using backdrop-filter: blur(12px) saturate(1.5). Each card should have a semi-transparent white background (rgba(255, 255, 255, 0.15)), a subtle white border (rgba(255, 255, 255, 0.25)), rounded corners of 16px, and a soft box shadow. Place a fixed navigation bar at the top with a darker frosted glass effect using backdrop-filter: blur(20px) brightness(0.9) and a dark semi-transparent background. On hover, each card should slightly lift upward (using translateY(-4px)) and increase its box shadow. Ensure all backdrop-filter declarations include the -webkit- prefix for Safari compatibility. Add a modal overlay that uses backdrop-filter: blur(8px) to blur the entire page behind it when active.