<div class="outline"></div>

<div id="controls"></div>

<div id="block"></div>



<textarea id="result">
  
</textarea>

#block {
  filter: url(#smooth-edges) ;
  
  --border-color: #fffa; 
  --shadow-color: #000; 
  
  --top-protrusion-width: 30%;
  --top-protrusion-height: 30px;
  
  --bottom-protrusion-width: 40%;
  --bottom-protrusion-height: 20px;
  
  
  min-height: calc(
    var(--top-protrusion-height) 
    + var(--bottom-protrusion-height)
    + var(--border-width, 0px) * 2 
    + 20px
  );
}

#block:after {
  content: "";
  display: block;
  bottom: var(--filter-margin-fix, 0);
  left: var(--filter-margin-fix, 0);
  top: var(--filter-margin-fix, 0);
  right: var(--filter-margin-fix, 0);
  background: linear-gradient(#fff7,#000);
  clip-path: polygon(
    calc(100% - var(--bottom-protrusion-width)) 0%, 
    100% 0, 
    100% calc(100% - var(--bottom-protrusion-height)), 
    var(--top-protrusion-width) calc(100% - var(--bottom-protrusion-height)), 
    var(--top-protrusion-width) 100%,
    0 100%, 
    0 var(--top-protrusion-height), 
    calc(100% - var(--bottom-protrusion-width)) var(--top-protrusion-height)
  );
  position: absolute;
  z-index: -1;
}


















body {
  padding: 10px;
  margin: 0;
  background: linear-gradient(90deg, purple, red)
}

.outline, #block {
  margin: 0 4vw;
  position: absolute;
  top: 50px;
  height: calc(90vh - 50px);
  width: 90vw;
}

.outline {
  outline:4px solid  #00f;
}

.test {
  position: absolute;
  width: 150px;
  height: 60px;
  left: 100px;
  top: 5px;
  background: url('https://images.unsplash.com/photo-1561736778-92e52a7769ef?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');
  border: 8px solid var(--color-highlight);
  border-radius: 25px;
  
}

:root {
  --color-highlight: #71c150;
}

input, textarea {
  border: 1px solid;
  color: #fff;
  background: #000;
}
#result {
  margin-left: 4vw;
  margin-top: 90vh;
  height: 90vh;
  width: 90vw;
}

#controls {
  margin-left: 4vw;
  color: #fff;
  background: #000;
}




const GAUSSIAN_BLUR_SPREAD = 2.8; // ~3

function createFilter({borderRadius, borderWidth, shadow}) {

  const [
    shadowX, 
    shadowY, 
    shadowDeviation
  ] = Object.assign([0,0,0], shadow.match(/\d+(\.\d+)?/g)?.map(Number)) ?? [8, 8, 0];

  const blur = parseInt(borderRadius, 10);
  const border = parseInt(borderWidth, 10);

  const erode = blur;
  const margin = -(erode - border / 2);

  let tableLength = Math.ceil(blur * GAUSSIAN_BLUR_SPREAD);
  let tableSpace = (tableLength - border) / 2;
  let ceilTableSpace = Math.ceil(tableSpace);
  let floorTableSpace = Math.floor(tableSpace);
  //if (tableSpace % 2) {
  //  tableLength *= 2;
  //} else {
 //   tableSpace = tableSpace / 2 | 0;
  //}

  function arr(length, fill) {
    return Array.from({length}, () => fill);
  }

  let borderTable = [
    ...arr(floorTableSpace, 0),
    ...arr(tableLength- ceilTableSpace - floorTableSpace , 1),
    ...arr(ceilTableSpace, 0),
  ].join(' ');

  let imageTable = [
    ...arr(floorTableSpace, 1),
    ...arr(tableLength - ceilTableSpace - floorTableSpace , 1),
    ...arr(ceilTableSpace, 0),
  ].join(' ');

  let shadowTable = [
    ...arr(floorTableSpace, 0),
    ...arr(tableLength - ceilTableSpace - floorTableSpace , 1),
    ...arr(ceilTableSpace, 1),
  ].join(' ');

  let filter = `
<!-- removing opacity -->
<feComponentTransfer in="SourceAlpha">
  <feFuncA type="linear" slope="1000"/>
</feComponentTransfer>

<!-- shrinking so we can crop inside -->
<feMorphology operator="erode" radius="${erode}"/>

<!--bluring so we have smothed edges -->
<feGaussianBlur stdDeviation="${blur}" result="blur"></feGaussianBlur>

<!--border-->
<feFlood flood-color="var(--border-color)" result="borderColor"/>
<feComponentTransfer in="blur" result="border">
  <feFuncA type="table" tableValues="${borderTable}"/>
</feComponentTransfer>
<feComposite in="borderColor" operator="in" result="border"/>

<!--shadow-->
<feComponentTransfer in="blur" result="cropedShadow">
  <feFuncA type="table" tableValues="${shadowTable}"/>
</feComponentTransfer>
<feDropShadow dx="${shadowX}" dy="${shadowY}" stdDeviation="${shadowDeviation}" flood-color="var(--shadow-color)" />
<feComposite in2="cropedShadow" operator="out" result="shadow" />

<!--image-->
<feComponentTransfer in="blur">
  <feFuncA type="table" tableValues="${imageTable}"/>
</feComponentTransfer>
<feComposite in="SourceGraphic" operator="out" result="cropedImage"/>

<feMerge>
  <feMergeNode in="shadow" />
  <feMergeNode in="border" />
  <feMergeNode in="cropedImage" />
</feMerge>
`

  block.style.setProperty('--filter-margin-fix', `${margin}px`);
  block.style.setProperty('--border-width', `${border}px`);

  block.innerHTML = rawPad`  
<svg style="visibility: hidden; position: absolute;" width="0" height="0" xmlns="http://www.w3.org/2000/svg" version="1.1" color-interpolation-filters="sRGB">
  <defs>
    <filter id="smooth-edges">
      ${filter}
    </filter>
  </defs>
</svg>
`;


  result.value = block.outerHTML;
}


function trimLinesLeft(str) {
  return str.replace(/^([^\S\r\n]*[\r\n]+)+/, '');
}
function trimLinesRight(str) {
  return str.replace(/([\r\n]+[^\S\r\n]*)+$/, '');
}
function trimLines(str) {
  return trimLinesRight(trimLinesLeft(str));
}

function rawPad({raw}, ...args) {
  const trim = true;
  const {length} = args;
  const strings = [...raw];

  //strings[0] = trimLinesLeft(strings[0]);
  //strings[strings.length-1] = trimLinesRight(strings[strings.length-1]);

  return strings.reduce((acc, value, i) => {
    let arg = i <= length ? trimLines(String(args[i-1])) : '';

    const space = acc.slice(acc.lastIndexOf('\n') + 1).replace(/[^\t]/g, ' ');

    let lines = arg.split(/(\r?\n)/);
    let linesLength = lines.length;

    if (space && linesLength > 1) {
      if (trim) {
        const line = lines[0];
        let toTrimLength = line.match(/^\s*/)[0].length;

        if (toTrimLength) {
          for(let i = 2; i < lines.length; i += 2) {
            const lineLength = lines[i].length;

            if (toTrimLength > lineLength) 
              toTrimLength = lineLength;

            for(let j = 0; j < toTrimLength; j++) {
              if (line[j] !== lines[i][j]) {
                toTrimLength = j;
                break;
              } 
            }

            if (!toTrimLength) break;
          }

          if (toTrimLength) {
            for(let i = 0; i < lines.length; i += 2) {
              lines[i] = lines[i].slice(toTrimLength);
            }
          }
        }
      }

      for(let i = 2; i < lines.length; i += 2) {
        lines[i] = space + lines[i];
      }

      return acc + lines.join('') + value;
    }

    return acc + arg + value;
  })
}



function drawControls() {
  const controls = [
    {
      type: 'text',
      name: 'shadow',
      value: '8 8 0'
    },
    {
      type: 'number',
      name: 'borderRadius',
      value: '8',
      valid({borderWidth}) {
        const min = (+borderWidth + 1) / 2 | 0;
       
        if (+this.value < min) {
          this.value = min;
        }
      }
    },
    {
      type: 'number',
      name: 'borderWidth',
      value: '4',
      valid({borderRadius}) {
        const max = borderRadius * 2 - 1;
        const min = 0;
        if (+this.value > max) {
          this.value = max;
        }
        if (+this.value < min) {
          this.value = min;
        }
      }
    }
  ];

  document.getElementById('controls').insertAdjacentHTML(
    'afterbegin',                                   
    controls.map(({name, type, value}) => rawPad`
      <label>
        ${name}
        <input type="${type}" id=${name} value="${value}"/>
      </label>
    `).join('\n'));

  const obj = {};
  controls.forEach(({name, value, valid}) => {
    obj[name] = value;
    document.getElementById(name).addEventListener('input', ({currentTarget}) => {
      valid?.call(currentTarget, obj);
      obj[name] = currentTarget.value;
      createFilter(obj);
    });
  });
  createFilter(obj);
}
drawControls() 

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.