I worked on a website where many images should have an inner shadow. To not need to edit all of them in Photoshop, the “inset” value for the CSS3 attribute “box-shadow” came in handy, but it cannot be applied to images without more ado. I want to show my solution here.
Applied directly on img
elements, the shadow stays behind the image, so it is only visible at all if the image has transparency. If you add a wrapper element which gets the shadow, it is still behind the image but can be positioned with z-index
. Unfortunately the image has to be moved to the back, moving the wrapper to the front is not possible 1:
1) Wrapper with Box Shadow, Image in The Background
HTML
<div class="box-shadow"> <img src="/images/graphic.jpg" /> </div>
CSS
.box-shadow { box-shadow: 0 0 10px 6px white inset; } .box-shadow img { position: relative; z-index: -1; }
The problem with this code is that the image is not only placed behind the wrapper but behind all other elements in the same Stacking Context. Which elements form a stacking context is described quite well in the Mozilla Documentation:
- the root element (HTML),
- positioned (absolutely or relatively) with a z-index value other than “auto”,
- a flex item with a z-index value other than “auto”,
- elements with an opacity value less than 1. (See the specification for opacity),
- elements with a transform value other than “none”,
- elements with a mix-blend-mode value other than “normal”,
- elements with isolation set to “isolate”,
- on mobile WebKit and Chrome 22+, position: fixed always creates a new stacking context, even when z-index is “auto”
- specifing any attribute above in will-change even you don’t write themselves directly
2) Stacking Context
In this case it was applicable to assign a z-index and relative positioning to the direct parent element of the wrapper (let’s call it #parent
):
CSS
#parent { position: relative; z-index: 0; }
If this is not possible, e.g. because it needs a different position value or the z-index has unwanted side effects, I would use opacity instead, with a value of almost 1, so that no effect is visible but it still creates a stacking context.
CSS (alternativ)
#parent { opacity: 0.999; }
Notes:
- Can anybody explain why? Obviously I still don’t 100% get z-index. ↩
Fabian Schmengler, thanks for beautiful post
I’m trying to add an inset box shadow to two images using their li elements, and to remove this shadow on the images on :hover. However, Im having a great deal of trouble positioning the shadow directly on top of the image. Not only does it sit slightly off the bottom of it, on one image the box shadow nearly extends to the top of the website. I assume this a problem with a container div I am using, but I have been unable to rectify it. Any thoughts?
here is my html code
Welcome to Walsh’s Window Cleaning
Lorem ipsum d….
Ut lorem odi….
Unfortunately the HTML code was swallowed by WordPress. Can you share the code with jsfiddle.net ?