What Are CSS Masks?
CSS masking is a powerful technique that enables developers to control exactly which portions of an element are visible, going beyond the rectangular constraints of the traditional box model. By applying a mask--essentially a visibility template--designers can create non-rectangular shapes, fade elements at edges, create complex visual effects, and build more engaging user interfaces.
Key capabilities include:
- Creating non-rectangular shapes without complex SVG or Canvas
- Fading content at edges for smooth overlays
- Combining multiple mask layers for complex effects
- Animating reveal effects and interactive unmasking
Modern web applications leverage CSS masking extensively for hero sections, image galleries, navigation menus, and interactive storytelling experiences. Unlike older techniques that required JavaScript libraries or SVG hacks, native CSS masks provide hardware-accelerated performance and browser-optimized rendering. The technique works by using an image to determine visibility on a per-pixel basis--opaque regions reveal the underlying element while transparent regions hide it entirely. This pixel-level control opens possibilities that would otherwise require complex image compositing or SVG manipulation.
From a performance perspective, CSS masks are handled by the browser's compositor, meaning smooth 60fps animations are achievable even on mobile devices. The properties integrate seamlessly with existing CSS workflows, using familiar syntax from background and border-radius patterns. Whether you're building a sleek product showcase with fade effects or creating an immersive scroll-triggered reveal, CSS masks provide the foundation for modern visual storytelling on the web. To learn more about creating performant visual effects, explore our CSS declaration guide for additional optimization techniques.
Understanding the Alpha Channel
The alpha channel represents the opacity or transparency information in an image. In digital imaging, each pixel typically contains color information (red, green, and blue channels) plus an alpha value that indicates how opaque or transparent that pixel should be.
Alpha Values Explained
- Alpha 1.0 (255): Fully opaque -- the pixel is completely visible
- Alpha 0: Fully transparent -- the pixel is completely hidden
- Values between 0-1: Semi-transparent -- partial visibility with underlying content showing through
When a CSS mask uses alpha mode, the mask's alpha values directly determine visibility: opaque regions reveal the element, transparent regions hide it, and semi-transparent regions create partial visibility effects.
Alpha vs Luminance Masks
CSS supports two distinct mask modes:
Alpha masks use only the transparency information--the color values are ignored entirely. This mode is intuitive and widely used because it directly maps transparency to visibility. The mask image's alpha channel becomes the visibility map for your element.
Luminance masks calculate visibility based on brightness (0.2126 × R + 0.7152 × G + 0.0722 × B), multiplied by the alpha value. Dark colors create transparent regions while bright colors create visible ones. This mode is particularly useful when working with grayscale images or when you want brightness to control visibility regardless of transparency.
/* Alpha mode - transparency directly controls visibility */
.alpha-mask {
mask-image: url('transparent-cutout.png');
mask-mode: alpha;
}
/* Luminance mode - brightness controls visibility */
.luminance-mask {
mask-image: url('grayscale-pattern.png');
mask-mode: luminance;
}
/* Auto-detect based on source (default) */
.auto-mask {
mask-image: url('mask.png');
mask-mode: match-source;
}
The distinction becomes critical when working with images that have both color and transparency. An RGBA PNG with a transparent background naturally works in alpha mode, while a grayscale image often works better in luminance mode. For most gradient-based masks, alpha mode provides the expected behavior with smooth transitions from opaque to transparent regions.
The mask-image Property
The mask-image property is the starting point for any CSS masking effect. It accepts various types of image sources:
Using CSS Gradients as Masks
CSS gradients are particularly well-suited for masking because they can create smooth transitions from opaque to transparent regions without additional network requests.
/* Simple gradient fade mask */
.fade-mask {
mask-image: linear-gradient(to right, black 0%, transparent 100%);
}
/* Circular reveal using radial gradient */
.circle-mask {
mask-image: radial-gradient(circle, black 50%, transparent 70%);
}
/* Complex shape with multiple stops */
.intricate-mask {
mask-image: linear-gradient(
45deg,
black 25%,
transparent 25%,
transparent 75%,
black 75%
);
}
Using Images as Masks
/* PNG mask with alpha channel */
.image-mask {
mask-image: url('mask-shape.png');
mask-repeat: no-repeat;
mask-position: center;
}
/* SVG mask element reference */
.svg-mask {
mask-image: url('masks.svg#heart-mask');
}
CORS Requirements: When using external images as mask sources, they must be served over HTTP or HTTPS protocols--local file:// URLs are blocked due to browser security restrictions. The server hosting the mask image must include appropriate CORS headers (Access-Control-Allow-Origin) if the mask is on a different domain. For SVG masks, the SVG file must be valid and well-formed XML.
Best Practices by Source Type:
- Gradients: Best for fade effects, simple shapes, and performance-critical animations. No network request needed.
- PNG Images: Ideal for complex organic shapes with soft edges. Ensure the PNG has a proper alpha channel.
- SVG Masks: Perfect for sharp vector shapes that scale without quality loss. Reference specific mask elements using fragment identifiers.
- CSS Functions: Consider conic-gradient for angular effects or repeating-linear-gradient for patterned masks.
Multiple mask layers can be combined by comma-separating values in mask-image, with subsequent layers composited according to mask-composite rules. For more advanced CSS property combinations, see our guide on CSS contain property to understand how layout containment interacts with masking techniques.
Positioning and Sizing Mask Layers
Controlling Position with mask-position
The mask-position property works identically to background-position, allowing you to specify where the mask image should be placed.
/* Position mask at top-left corner */
.top-left-mask {
mask-image: url('pattern-mask.png');
mask-position: top left;
}
/* Offset mask from edges */
.offset-mask {
mask-image: url('border-mask.png');
mask-position: 20px 30px;
}
Sizing with mask-size
/* Stretch to cover entire element */
.cover-mask {
mask-image: url('icon-mask.png');
mask-size: cover;
}
/* Contain within element bounds */
.contain-mask {
mask-image: url('logo-mask.png');
mask-size: contain;
}
/* Specific dimensions */
.fixed-mask {
mask-image: url('corner-decoration.png');
mask-size: 100px 150px;
}
Mask Repeat Patterns
/* Tile horizontally */
.horizontal-pattern {
mask-image: url('stripe-mask.png');
mask-repeat: repeat-x;
mask-size: auto 100%;
}
/* Space tiles without clipping */
.spaced-mask {
mask-image: url('dot-mask.png');
mask-repeat: space;
mask-size: 30px 30px;
}
Multiple Mask Layers in Combination
When using multiple mask layers, each position, size, and repeat value can be specified comma-separated to control each layer independently. The first value applies to the first layer, second to the second, and so on.
/* Layer 1: full-cover gradient, Layer 2: centered pattern */
.multi-layer-mask {
mask-image: linear-gradient(to right, black, transparent),
url('pattern.png');
mask-position: center, center;
mask-size: cover, 50px 50px;
mask-repeat: no-repeat, repeat;
mask-composite: add;
}
This layering system enables sophisticated effects like adding a pattern overlay to a gradient fade, or combining a shape cutout with a gradient edge. The order matters--earlier layers serve as the base, and subsequent layers modify visibility through the composite operation. Understanding this stacking context is essential for predictable masking results.
Controlling Mask Clipping and Origin
The mask-clip Property
The mask-clip property determines the area that the mask affects.
/* Mask applies to border-box (default) */
.border-box-mask {
mask-image: url('shape-mask.png');
mask-clip: border-box;
}
/* Mask applies only to content area */
.content-box-mask {
mask-image: url('shape-mask.png');
mask-clip: content-box;
}
/* Mask applies to padding area */
.padding-box-mask {
mask-image: url('shape-mask.png');
mask-clip: padding-box;
}
/* Mask applies to fill-box (object bounding box) */
.fill-box-mask {
mask-image: url('shape-mask.png');
mask-clip: fill-box;
}
The mask-origin Property
The mask-origin property establishes the origin box for mask positioning.
/* Position relative to border box */
.border-origin {
mask-image: url('corner-mask.png');
mask-origin: border-box;
mask-position: top left;
}
/* Position relative to content box */
.content-origin {
mask-image: url('corner-mask.png');
mask-origin: content-box;
mask-position: 10px 10px;
}
Visual Difference Between Clip and Origin
The key distinction is that mask-clip defines where the mask applies (the affected area), while mask-origin defines where positioning starts (the reference point). Consider a button with a border and padding:
- border-box clip: The mask affects the entire button including border
- content-box clip: The mask affects only the inner content area
- border-box origin with 10px 10px offset: Positioning starts from the border edge
- content-box origin with 10px 10px offset: Positioning starts from the content edge
When mask-clip and mask-origin have different values, interesting offset effects emerge. For example, positioning a mask relative to the content box while clipping to the border box can create reveal effects that extend beyond the content area. The fill-box value is particularly useful for replaced elements like images and videos, where the mask should conform to the object's natural shape rather than the rectangular element box.
Advanced: mask-mode and mask-composite
Understanding mask-mode
While the default behavior (match-source) automatically determines the appropriate mode, mask-mode gives you explicit control.
/* Explicitly use alpha mode */
.alpha-mode {
mask-image: url('gradient-mask.png');
mask-mode: alpha;
}
/* Explicitly use luminance mode */
.luminance-mode {
mask-image: url('grayscale-mask.png');
mask-mode: luminance;
}
Combining Layers with mask-composite
The mask-composite property controls how multiple mask layers combine. The first image in the mask-image list serves as the base layer, and subsequent layers are combined with it using the specified operation.
/* Add mask values together (default) - union of visible regions */
.add-masks {
mask-image: url('layer1.png'), url('layer2.png');
mask-composite: add;
}
/* Subtract subsequent layers from first */
.subtract-masks {
mask-image: url('base-mask.png'), url('cutout-mask.png');
mask-composite: subtract;
}
/* Intersect mask values - only overlapping regions visible */
.intersect-masks {
mask-image: url('shape1.png'), url('shape2.png');
mask-composite: intersect;
}
/* Exclude overlapping regions - union minus intersection */
.exclude-masks {
mask-image: url('shape1.png'), url('shape2.png');
mask-composite: exclude;
}
Composite Operations Visualized:
- add: Both shapes visible--their union forms the visible area
- subtract: Only the first shape is visible; the second cuts a hole where they overlap
- intersect: Only the overlapping region between shapes is visible
- exclude: Both shapes visible, but their overlapping region becomes hidden
This compositing system enables complex masking effects without additional image editing. For instance, creating a cutout effect is as simple as adding a subtract composite layer with your cutout shape. If you're working with canvas-based effects, our guide on pixel manipulation with Canvas covers complementary techniques for direct pixel control.
Practical Examples and Use Cases
Image Reveal Effects
Create progressive reveals and interactive unmasking effects using animated mask positions.
/* Hover-triggered reveal */
.reveal-effect {
mask-image: linear-gradient(to right, transparent, black 20%);
mask-size: 200% 100%;
mask-position: left;
transition: mask-position 0.3s ease;
}
.reveal-effect:hover {
mask-position: right;
}
/* Scroll-triggered reveal */
.scroll-reveal {
mask-image: linear-gradient(to top, black 0%, transparent 100%);
mask-size: 100% 200%;
mask-position: bottom;
}
/* JavaScript activates scroll reveal
element.classList.add('active') moves mask-position to top */
.scroll-reveal.active {
mask-position: top;
}
Text Mask Effects
Fill text with images or gradients while keeping the text shape visible.
/* Gradient text fill effect */
.gradient-text {
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
/* Animated gradient text */
.animated-gradient-text {
background: linear-gradient(
90deg,
#ff6b6b, #feca57, #48dbfb, #ff9ff3, #ff6b6b
);
background-size: 200% auto;
background-clip: text;
color: transparent;
animation: gradient-shift 3s linear infinite;
}
@keyframes gradient-shift {
to { background-position: 200% center; }
}
/* Image-filled text with additional mask */
.image-filled-text {
background-image: url('texture.jpg');
background-clip: text;
color: transparent;
mask-image: url('text-outline-mask.png');
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
}
Responsive Image Shapes
Create adaptive non-rectangular image shapes using polygon or image-based masks.
/* Angular image edges */
.angled-image {
mask-image: polygon(
0% 0%, 100% 0%, 100% 85%, 95% 100%, 5% 100%, 0% 85%
);
}
/* Organic shape with SVG mask */
.organic-image {
mask-image: url('organic-shapes.svg#blob-1');
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
}
/* Responsive shape switching */
.responsive-mask {
mask-image: url('desktop-mask.png');
mask-size: cover;
mask-position: center;
}
@media (max-width: 768px) {
.responsive-mask {
mask-image: url('mobile-mask.png');
}
}
Interactive Card Effects
Combine masking with hover states for engaging card interactions.
/* Card with directional reveal */
.feature-card {
position: relative;
overflow: hidden;
}
.feature-card::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
mask-image: linear-gradient(to bottom, black 50%, transparent 100%);
mask-size: 100% 200%;
mask-position: top;
transition: mask-position 0.4s ease;
}
.feature-card:hover::before {
mask-position: bottom;
}
Best Practices for CSS Masking
Performance Optimization
- Prefer animating position/size over changing images -- Position and size changes are more efficiently handled by the browser's compositor
- Minimize mask layers -- Each additional layer increases rendering complexity
- Use simple masks when possible -- Hard edges are faster to render than many semi-transparent regions
- Consider GPU acceleration -- Animating mask-position and mask-size typically runs on the compositor thread
/* Efficient animation - animates position on compositor */
.efficient-animation {
mask-image: url('large-mask.png');
mask-size: 200% 100%;
mask-position: left;
transition: mask-position 0.3s ease;
}
/* Less efficient - triggers layout recalculation */
inefficient-animation {
mask-image: url('mask-left.png');
transition: mask-image 0.3s ease;
}
Browser Support
CSS masking has excellent modern browser support with baseline availability since 2023 across Chrome, Edge, Firefox, and Safari.
/* Graceful degradation */
.masked-element {
border-radius: 8px;
overflow: hidden;
}
@supports (mask-image: url('mask.png')) {
.masked-element {
mask-image: url('mask.png');
border-radius: 0;
}
}
Accessibility Considerations
Masking is purely visual and doesn't affect the underlying DOM or accessibility tree, but certain practices ensure inclusive experiences:
-
Maintain Hit Areas: Ensure masked interactive elements (buttons, links) maintain proper clickable areas. Test that touch targets remain functional after masking.
-
Don't Obscure Content: Semi-transparent masks shouldn't hide important text, form fields, or interactive controls. Users must be able to read and interact with all essential content.
-
Keyboard Navigation: Verify that masked buttons remain focusable and have visible focus states. The visual shape shouldn't affect keyboard accessibility.
-
Screen Reader Testing: Run tests with screen readers to ensure masked content is announced correctly. Masking doesn't hide content from assistive technologies.
-
Reduced Motion: Respect user preferences for reduced motion when animating masks.
/* Respect reduced motion preferences */
.reveal-animation {
mask-size: 200% 100%;
mask-position: left;
transition: mask-position 0.3s ease;
}
@media (prefers-reduced-motion: reduce) {
.reveal-animation {
transition: none;
mask-position: right;
}
}
Testing Recommendations:
- Test mask animations across target browsers and devices
- Verify masked forms remain fully functional
- Check masked content at different zoom levels
- Test with high contrast mode enabled
Common Patterns and Recipes
Fade Edges
/* Horizontal fade */
.fade-edges {
mask-image: linear-gradient(
to right,
transparent 0%,
black 15%,
black 85%,
transparent 100%
);
}
/* Vertical fade */
.fade-edges-vertical {
mask-image: linear-gradient(
to bottom,
transparent 0%,
black 20%,
black 80%,
transparent 100%
);
}
/* Corner fade for spotlight effects */
.spotlight-mask {
mask-image: radial-gradient(
circle at top right,
black 40%,
transparent 70%
);
}
Polygonal Shapes
/* Diamond */
.diamond-mask {
mask-image: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}
/* Hexagon */
.hexagon-mask {
mask-image: polygon(
25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%
);
}
/* Star */
.star-mask {
mask-image: polygon(
50% 0%, 61% 35%, 98% 35%, 68% 57%,
79% 91%, 50% 70%, 21% 91%, 32% 57%,
2% 35%, 39% 35%
);
}
/* Chevron arrow */
.chevron-mask {
mask-image: polygon(
20% 40%, 50% 70%, 80% 40%, 85% 50%,
55% 80%, 50% 85%, 45% 80%, 15% 50%
);
}
Animated Pulse Effect
.pulse-mask {
mask-image: radial-gradient(circle, black 30%, transparent 70%);
mask-size: 200% 200%;
mask-position: center;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { mask-size: 100% 100%; }
50% { mask-size: 200% 200%; }
}
Complex Composite Patterns
/* Layered fade with pattern overlay */
.pattern-fade {
mask-image: linear-gradient(to bottom, black, transparent),
url('dot-pattern.png');
mask-size: 100% 100%, 20px 20px;
mask-repeat: no-repeat, repeat;
mask-composite: add;
}
/* Cutout effect using subtract composite */
.cutout-card {
mask-image: url('card-shape.png'), url('logo-cutout.png');
mask-composite: subtract;
}
Interactive Button Patterns
/* Button with animated border reveal */
.cta-button {
position: relative;
background: linear-gradient(90deg, #667eea, #764ba2);
color: white;
padding: 1rem 2rem;
border-radius: 4px;
}
.cta-button::before {
content: '';
position: absolute;
inset: 0;
background: white;
mask-image: linear-gradient(
to right,
transparent 0%,
black 0%,
black 100%,
transparent 100%
);
mask-size: 200% 100%;
mask-position: left;
transition: mask-position 0.4s ease;
}
.cta-button:hover::before {
mask-position: right;
}
Frequently Asked Questions
Build Stunning Visual Effects with Modern CSS
Our team of expert developers specializes in creating engaging user experiences with cutting-edge CSS techniques including masking, animations, and interactive effects. From performance-optimized animations to accessible interactive components, we build web experiences that delight users and drive results.