Артефакты прорисовки теней в рамке с уголком

144
02 мая 2019, 00:00

Надо мне сверстать рамку с уголком подобного вида:

Подобных вопросов и на ru so и в сети находится достаточно, чтобы на чистом css и без картинок. Сделал по гайдлайнам что-то типа:

    .sticky { 
        width: 20rem; 
    } 
 
    .sticky div { 
        position:relative; 
        background-color: #FFFFCC; 
        padding: 1rem 2rem; 
        box-shadow:0 1px 1px rgba(0,0,0,0.2), -1px 1px 1px rgba(0,0,0,0.1); 
    } 
     
    .sticky div:before { 
        content:""; 
        position:absolute; 
        top:0; 
        right:0; 
        border-width:0 16px 16px 0; 
        border-style:solid; 
        border-color:#FFFF99 #fff; 
        box-shadow:0 1px 1px rgba(0,0,0,0.1), -1px 1px 1px rgba(0,0,0,0.01); 
    }
<div class="row"> 
    <div class="col-12 col-sm-12 col-md-4"> 
        <div class="sticky"> 
            <div> 
                <h3>Some text here</h3> 
                <p>Phone: 322 223</p> 
                <p>Email: <a href="mailto:test@example.tld">test@example.tld</a></p> 
            </div> 
        </div> 
    </div> 
</div>

Я не понял, как сделать диагональную линию, но в принципе, накидав побольше теней получил вполне устраивающий результат в Firefox:

Но когда я открываю это всё в Chrome я вижу какую-то уродливую тень, заметную невооружённым глазом (потом пересматривая в FF под лупой вижу, что и там она есть, но не так сильно бросается в глаза)

Выключена внешняя тень, оставлена тень от уголка:

Выключена тень от уголка, оставлена внешняя тень:

Без теней листочек получается какой-то бедный с изобразительной точки зрения, ненастоящий, поэтому у меня возникает следующий вопрос: как кроссбраузерно сделать тень только слева и снизу и чтобы сверху и справа не было не было артефактов?

Answer 1
Box-shadow: blur & spread

Используя свойство box-shadow, чаще всего указывают всего три параметра: смещение по X, смещение по Y и размер размытия (blur). При этом незаслуженно забывая ещё об одном параметре — spread.
В разных мануалах, его называют по-разному — распространение или сжатие — скорее всего, это потому, что параметр может принимать, как положительные значения, так и отрицательные, в зависимости от задач. А также потому, что например, в Chrome этот параметр нельзя выставить меньше нуля нативным средством в инспекторе стилей (хотя, прописав значение вручную, всё прекрасно отображается и работает, если не двигать ползунок):

Так что же делает этот параметр? Интерактивный пример, расставляет всё по местам:

.controls{display:flex;justify-content:space-around;text-align:center}.controls input{display:block}:root{--cx:0;--cy:0;--sb:50px;--ss:0}.example{margin:15px auto;width:350px;height:70px;line-height:70px;text-align:center;box-sizing:border-box;border:4px solid #fa0;background-color:rgba(255,255,255,0);box-shadow:var(--cx) var(--cy) var(--sb) var(--ss) rgba(0,0,0,0.5)}
<div class="controls" oninput="document.querySelector('.example').innerText=getComputedStyle(document.querySelector('.example')).boxShadow;"> <div>offset_X (-n...n)<input type="range" min="-100" max="100" oninput="document.documentElement.style.setProperty('--cx', this.value + 'px');">смещение по X</div><div>offset_Y (-n...n)<input type="range" min="-100" max="100" oninput="document.documentElement.style.setProperty('--cy', this.value + 'px');">смещение по Y</div><div>blur (0...n)<input type="range" min="0" max="100" oninput="document.documentElement.style.setProperty('--sb', this.value + 'px');">размытие</div><div>spread (-n...n)<input type="range" min="-100" max="100" oninput="document.documentElement.style.setProperty('--ss', this.value + 'px');">распространение</div></div><div class="example">rgba(0, 0, 0, 0.5) 0px 0px 50px 0px</div>

Подведём итоги наблюдений:

  • Смещения — могут быть, как положительными, так и отрицательными;
  • Размытие — это не размер тени, хотя и влияет на её размер визуально. Размытие как бы смазывает границу тени. Может принимать только положительное значение;
  • Распространение — это размер тени, относительно блока, который её отбрасывает. Может принимать, как положительные, так и отрицательные значения. Отрицательное значение не может быть меньше половины меньшего из размеров блока. Например, если height блока 70px, то при значении spread меньше -34px тень просто схлопнется (70 / 2 - 35 = 0). При этом, исчезнет и её размытие, каким бы большим оно ни было, т.к. размывать нечего.

После небольшого отступления в теорию, переходим к ответу на вопрос:

body { 
  background-color: #d4ebf7; 
} 
.sticky, 
.sticky_right { 
  /* Здесь добавил и изменил стили только для сниппета */ 
  width: 18rem; 
  float: left; 
  margin: 3px; 
} 
 
/********************** 
* Исправленный стикер * 
**********************/ 
.sticky div { 
  position: relative; 
  background-color: #FFFFCC; 
  padding: 1rem 2rem; 
} 
.sticky div:before { 
  content: ''; 
  position: absolute; 
  top: 0; 
  right: 0; 
  z-index: 1; 
  border-width: 0 16px 16px 0; 
  border-style: solid; 
  border-color: #FFFF99 #fff; 
  box-shadow: -1px 2px 2px -1px rgba(0, 0, 0, 0.3); 
} 
.sticky div:after { 
  content: ''; 
  position: absolute; 
  bottom: 0; 
  left: 0; 
  width: 100%; 
  height: 100%; 
  z-index: -1; 
  box-shadow: -2px 3px 3px -1px rgba(0, 0, 0, 0.3); 
} 
 
/******************** 
* Правильный стикер * 
********************/ 
.sticky_right div { 
  position: relative; 
  /* Фон изменён со сплошного на градиент */ 
  background-image: linear-gradient(225deg, transparent 10px, #FFFFCC 10px); 
  padding: 1rem 2rem; 
} 
.sticky_right div:before { 
  content: ''; 
  position: absolute; 
  top: 0; 
  right: 0; 
  z-index: 1; 
  border-width: 0 16px 16px 0; 
  border-style: solid; 
  /* Цвет изменён с белого на прозрачный */ 
  border-color: #FFFF99 transparent; 
  box-shadow: -1px 2px 2px -1px rgba(0, 0, 0, 0.3); 
} 
.sticky_right div:after { 
  content: ''; 
  position: absolute; 
  bottom: 0; 
  left: 0; 
  width: 100%; 
  height: 100%; 
  z-index: -1; 
  box-shadow: -2px 3px 3px -1px rgba(0, 0, 0, 0.3); 
}
<div class="row"> 
  <div class="col-12 col-sm-12 col-md-4"> 
    <div class="sticky"> 
      <div> 
        <h3>Some text here</h3> 
        <p>Phone: 322 223</p> 
        <p>Email: <a href="mailto:test@example.tld">test@example.tld</a></p> 
      </div> 
    </div> 
    <div class="sticky_right"> 
      <div> 
        <h3>Some text here</h3> 
        <p>Phone: 322 223</p> 
        <p>Email: <a href="mailto:test@example.tld">test@example.tld</a></p> 
      </div> 
    </div> 
  </div> 
</div>

Алгоритм прост, изменения минимальны:

  1. :before — в свойстве box-shadow с помощью параметра spread уменьшаем размер тени, но при этом сдвигаем тень ещё больше, в том же направлении, куда она была сдвинута ранее. Добавляем положительный z-index, на всякий случай.
  2. :after — добавляем. Выравниваем по левому и нижнему краю. Растягиваем по размерам родителя. Позиционируем с помощью отрицательного z-index за блоком. С box-shadow проводим те же манипуляции, что и с предыдущим блоком — уменьшаем тень четвёртым параметром и сдвигаем влево вниз.

Таким образом, за счёт смещений, тень осталась на своём месте, но из-за отрицательного распространения, стала меньше и спряталась под основной блок на противоположных от направления смещений краях.

PS Т.к. была замечена, на мой взгляд, ещё одна недоработка — непрозрачность в месте отгиба — добавил рядом ещё вариант для сравнения. Кроссбраузерность напрямую зависит от поддержки linear-gradient.

READ ALSO
Не компилируется sass

Не компилируется sass

Подскажите пожалуйста, не компилируется sass в cssУстановил gulp, sass, прописал таск

156
Как переместить прокрутку влево

Как переместить прокрутку влево

Как переместить прокрутку влево, а картинку справа сделать на 100%?

166