Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to achieve this combination of inner shadow, rounded border, offset border, and corner-images using only CSS?

Tags:

html

css

border

svg

I am trying to set up a frame for <img> elements that automatically sizes itself to the inner image and takes its colors from the webpage's CSS variables, consisting of several parts:

  • an inner shadow on all edges of the image,
  • an outer border with rounded corners and variable-color --Theme_Outline,
  • an offset border 2.25rem outside the image on all four sides with square corners and variable-color --Theme_Decor (with the page background visible through the gap), and
  • four SVG decorations (one at each corner of the offset border) with variable-color --Theme_Decor.

Here's an example of what I want it to look like that I made in an art program, with a textured background and with pure cyan for variable-color --Theme_Outline and pure red for variable-color --Theme_Decor so they stand out:

Example image made in an art program

The only way I can find to do an inner shadow effect is with a CSS box-shadow with the inset attribute, but that gets rendered beneath the content of whatever element it is attached to (meaning beneath the image in an <img> element, for example), so to make it visible above the image the only option seems to be wrapping the image in a <div> with an ::after pseudo element.

With that constraint (wrapping every <img> in a <div class="bordered">) in mind, the best CSS code I've been able to come up with so far is:

:root{
  --Theme_Decor: #ff0000;
  --Theme_Outline: #00ffff;
}

html{
  position: relative;
  box-sizing: border-box;
  font-size: 16px;
  line-height: 1.5rem;
  background-image: url(https://i.sstatic.net/gfRXmBIz.png);
  background-size: cover;
  background-attachment: fixed;
}

.bordered{
  display: inline-block;
  position: relative;
  padding: 2.25rem;
  outline: 0.25rem solid var(--Theme_Outline);
  outline-offset: -2.25rem;
  border-radius: 2.25rem;
  line-height: 0;
}

.bordered::after{
  content: "";
  position: absolute;
  width: calc(100% - 4.5rem);
  height: calc(100% - 4.5rem);
  inset: 2.25rem;
  box-shadow: inset 0 0 0.375rem 0.25rem rgba(0, 0, 0, 0.5);
  outline: 0.0625rem solid var(--Theme_Decor);
  outline-offset: 2.1875rem;
}
<html>
  <div class="bordered">
    <img src="https://i.sstatic.net/2fkv9L8M.png">
  </div>
</html>

That does what I want for the first three of the four parts of my goal, missing only the corner decorations. Here's a screenshot from Firefox showing the result:

Image captured from browser with everything except the corner decorations

However, I can't figure out a way to insert the corner decorations within the CSS.

If I use url() to link static SVG files inside the content property, I can't find a way to position them at every corner and I can't use stroke="currentColor" or stroke="var(--Theme_Decor)" to get the desired color.

I also experimented with adding the ::before pseudo-element so I could apply the border-image property to it without disrupting the other three parts of the frame, but again couldn't figure out a way to make it pull the desired color.

Is my goal achievable purely with CSS? Or do I need to settle for adding extra HTML elements around every <img> that I want to be framed?

For completeness, here is the SVG code for the individual corner decorations, using the color black as a placeholder:

<svg id="UIT_Corner_Image_TopLeft" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" width="28px">
    <path d="M.5,27.5h1c3.86599325,0,7-3.13400675,7-7v-12h12c3.86599325,0,7-3.13400675,7-7V.5" fill="none" stroke="#000"/>
</svg>

<svg id="UIT_Corner_Image_TopRight" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 72" width="108px">
    <path d="M107.5,8.5l-100-0c-3.86599325,0-7-3.13400675-7-7V.5" fill="none" stroke="#000"/>
    <path d="M38.23828125,8.5c2.515625,4.75579834,7.50708008,8,13.26171875,8h40v40c0,8.28424072,6.71569823,15,15,15h1" fill="none" stroke="#000"/>
</svg>

<svg id="UIT_Corner_Image_BottomLeft" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 72" width="108px">
    <path d="M.5,63.5h100c3.86599325,0,7,3.13400676,7,7v1" fill="none" stroke="#000"/>
    <path d="M69.76171875,63.5c-2.515625-4.75579834-7.50708008-8-13.26171875-8H16.5V15.5c0-8.28424072-6.71569824-15-15-15H.5" fill="none" stroke="#000"/>
</svg>

<svg id="UIT_Corner_Image_BottomRight" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" width="28px">
    <path d="M27.5.5h-1c-3.86599325,0-7,3.13400675-7,7v12H7.5c-3.86599325,0-7,3.13400675-7,7v1" fill="none" stroke="#000"/>
</svg>

And here is the SVG code for the border-image version:

<svg id="UIT_Border_Image" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 224 160" width="224" height="160">
    <g opacity="0.75">
        <path d="M.5,27.5h1c3.86599325,0,7-3.13400675,7-7v-12h12c3.86599325,0,7-3.13400675,7-7V.5" fill="none" stroke="#000"/>
        <path d="M223.5,8.5h-100c-3.86599325,0-7-3.13400675-7-7V.5" fill="none" stroke="#000"/>
        <path d="M154.23828125,8.5c2.515625,4.75579834,7.50708008,8,13.26171875,8h40v40c0,8.28424072,6.71569824,15,15,15h1" fill="none" stroke="#000"/>
        <path d="M.5,151.5h100c3.86599325,0,7,3.13400676,7,7v1" fill="none" stroke="#000"/>
        <path d="M69.76171875,151.5c-2.515625-4.75579834-7.50708008-8-13.26171875-8H16.5v-40c0-8.28424072-6.71569824-15-15-15H.5" fill="none" stroke="#000"/>
        <path d="M223.5,132.5h-1c-3.86599325,0-7,3.13400675-7,7v12h-12c-3.86599325,0-7,3.13400675-7,7v1" fill="none" stroke="#000"/>
    </g>
</svg>

Plus an example of the CSS I came up with for using the border-image property, though using a PNG version because I couldn't find a way to reference the SVG file within the code snippet:

:root{
  --Theme_Decor: #ff0000;
  --Theme_Outline: #00ffff;
}

html{
  position: relative;
  box-sizing: border-box;
  font-size: 16px;
  line-height: 1.5rem;
  background-image: url(https://i.sstatic.net/gfRXmBIz.png);
  background-size: cover;
  background-attachment: fixed;
}

.bordered{
    display: inline-block;
    position: relative;
    padding: 2.25rem;
    outline: 0.125rem solid var(--Theme_Outline);
    outline-offset: -2.25rem;
    border-radius: 2.25rem;
    line-height: 0;
}

.bordered::before{
    content: "";
    position: absolute;
    width: calc(100% - 4.5rem);
    height: calc(100% - 4.5rem);
    left: 0;
    top: 0;
    border-style: solid;
    border-color: rgba(0, 0, 0, 0);
    border-width: 2.25rem 2.25rem;
    border-image-source: url(https://i.sstatic.net/Ums6HqNE.png);
    border-image-slice: 72 108;
    border-image-width: 4.5rem 6.75rem;
    outline: 0.0625rem solid var(--Theme_Decor);
    outline-offset: -0.0625rem;
}

.bordered::after{
    content: "";
    position: absolute;
    width: calc(100% - 4.5rem);
    height: calc(100% - 4.5rem);
    inset: 2.25rem;
    box-shadow: inset 0 0 0.375rem 0.25rem rgba(0, 0, 0, 0.5);
}
<html>
  <div class="bordered">
    <img src="https://i.sstatic.net/2fkv9L8M.png">
  </div>
</html>
like image 637
Lawton Avatar asked Nov 01 '25 12:11

Lawton


1 Answers

Here is an idea using mask where you can easily control the color by setting background. I am including the SVG as data images but you can also reference them as url if you want.

.bordered {
  --Theme_Decor: #ff0000;
  --Theme_Outline: #00ffff;
  
  display: inline-flex;
  position: relative;
  padding: 2.25rem;
  outline: 0.125rem solid var(--Theme_Outline);
  outline-offset: -2.25rem;
  border-radius: 2.25rem;
}

.bordered img {
  outline: 0.0625rem solid var(--Theme_Decor);
  outline-offset: 2.25rem;
}

.bordered::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--Theme_Decor); /* color of SVG here */
  mask:
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28"><path d="M.5,27.5h1c3.86599325,0,7-3.13400675,7-7v-12h12c3.86599325,0,7-3.13400675,7-7V.5" fill="none" stroke="black"/></svg>') 0 0,
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 72"><path d="M107.5,8.5l-100-0c-3.86599325,0-7-3.13400675-7-7V.5" fill="none" stroke="black"/><path d="M38.23828125,8.5c2.515625,4.75579834,7.50708008,8,13.26171875,8h40v40c0,8.28424072,6.71569823,15,15,15h1" fill="none" stroke="black"/></svg>') 100% 0,
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 72" width="108px"><path d="M.5,63.5h100c3.86599325,0,7,3.13400676,7,7v1" fill="none" stroke="black"/><path d="M69.76171875,63.5c-2.515625-4.75579834-7.50708008-8-13.26171875-8H16.5V15.5c0-8.28424072-6.71569824-15-15-15H.5" fill="none" stroke="black"/></svg>') 0 100%,
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28"><path d="M27.5.5h-1c-3.86599325,0-7,3.13400675-7,7v12H7.5c-3.86599325,0-7,3.13400675-7,7v1" fill="none" stroke="black"/></svg>') 100% 100%;
  mask-size: 2rem auto, 6rem auto, 6rem auto, 2rem auto; /* size of the SVG here */
  mask-repeat: no-repeat;
}

.bordered::after {
  content: "";
  position: absolute;
  inset: 2.25rem;
  box-shadow: inset 0 0 0.375rem 0.25rem rgba(0, 0, 0, 0.5);
}

html {
  background: url(https://i.sstatic.net/gfRXmBIz.png) 50%/cover fixed;
}

img {
  width: 200px;
}
<div class="bordered">
  <img src="https://i.sstatic.net/2fkv9L8M.png">
</div>

<div class="bordered" style="--Theme_Decor: blue">
  <img src="https://i.sstatic.net/2fkv9L8M.png">
</div>
like image 91
Temani Afif Avatar answered Nov 03 '25 03:11

Temani Afif