Using filter and box-shadow together
For use when exporting an HTML page containing filter to PDF using Chrome

We all know by now that a box-shadow is used to create a shadow around a DOM element like this : box-shadow:2px 3px 10px 0 rgba(0,0,0,.25); - same solution using filter like this filter: drop-shadow(2px 3px 10px rgba(0, 0, 0, 0.15)); but filter applies to the div and all its contents too as its an ‘atomic’ property. So if we want a shadow around a DIV only, we use a box-shadow.
But here are some notes on why I need box-shadow and filter together.
When exporting the HTML page to a PDF, macOS's default PDF viewer requires a filter as in filter: drop-shadow(2px 3px 10px rgba(0, 0, 0, 0.15)); otherwise it shows a box with a background colour behind the content.
This is a known rendering bug between Chromium's PDF engine and macOS Preview.
Claude : When box-shadow with transparency (rgba) is applied directly to child flex items (especially with percentage widths like 40%/60% or CSS gap), macOS Preview fails to calculate the alpha/clipping layer for those nested flex children, flattening the semi-transparent shadow into a solid gray block behind and around them.
Take this code as an example :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>filter and box-shadow</title>
<style>
*, *::before, *::after
{
box-sizing: border-box;
margin: 0;
padding: 0;
}
.container
{
margin: 20px;
}
#two-boxes
{
margin-top: 20px;
display: flex;
flex-direction: row;
gap: 20px;
}
#two-boxes > div
{
flex: 1;
display: flex;
flex-direction: column;
padding: 25px 20px;
border-radius:18px;
box-shadow:2px 3px 10px 0 rgba(0,0,0,.25);
filter: drop-shadow(2px 3px 10px rgba(0, 0, 0, 0.15));
}
#two-boxes > div > img
{
display: block;
margin: 15px auto 0;
filter: none;
box-shadow: none;
}
</style>
</head>
<body>
<div class="container">
<div id="two-boxes">
<div>
<img src="https://anjanesh.s3.us-east-1.amazonaws.com/blogs/images/coral-crouch.png" alt="" />
</div>
<div>
<img src="https://anjanesh.s3.us-east-1.amazonaws.com/blogs/images/teal-yoga.png" alt="" />
</div>
</div>
</div>
</body>
It shows a shadow around the 2 images too which I didn't ask for - even though I specified filter: none; and box-shadow: none; in #two-boxes > div > img - because filter gets 'passed' down to the child elements.
The solution, as pointed out by PaulOB from Sitepoint Community Forums is to place the filter in the parent container's pseudo element ::before
#two-boxes > div
{
flex: 1;
display: flex;
flex-direction: column;
padding: 25px 20px;
/*
border-radius:18px;
box-shadow:2px 3px 10px 0 rgba(0,0,0,.25);
filter: drop-shadow(2px 3px 10px rgba(0, 0, 0, 0.15));
*/
position: relative;
}
#two-boxes > div::before
{
content: "";
position: absolute;
inset: 0;
border-radius: 18px;
box-shadow: 2px 3px 10px 0 rgba(0, 0, 0, 0.25);
filter: drop-shadow(2px 3px 10px rgba(0, 0, 0, 0.15));
pointer-events: none;
/*z-index: -1;*/
}
Demo : https://anjanesh.s3.us-east-1.amazonaws.com/demo/filter.html


