Магический круг: CSS головоломка

    Доброго времени суток, уважаемые хабравчане. Недавно Hugo Giraudel, он же CSS гоблин, SASS хакер и Margin псих опубликовал в своем блоге очень интересную CSS задачку на смышленость.

    image

    Сможете заверстать подобное учитывая следующие правила?

    • Круг в центре должен быть прозрачным, чтобы был виден background
    • Расстояние между левыми и правыми блоками, как между верхними и нижними должно быть одинаковое
    • При наличии потомков в блоке, содержимое должно отображаться
    • DOM должен выглядеть следующим образом: ul > li > section > header + footer
    • Нельзя использовать JavaScript и изображения
    • Дополню еще от себя: нельзя использовать CSS Shape и Clip Path

    Чтобы не было очень лень — каркас уже есть.

    Сделали?


    Базовый HTML:
    <ul class="boxes">
      
      <li class="box  box-alpha">
        <section class="box-content">
          <header class="box-header"></header>
          <footer class="box-footer"></footer>
        </section>
      </li>
      
      <li class="box  box-beta">
        <section class="box-content">
          <header class="box-header"></header>
          <footer class="box-footer"></footer>
        </section>
      </li>
      
      <li class="box  box-gamma">
        <section class="box-content">
          <header class="box-header"></header>
          <footer class="box-footer"></footer>
        </section>
      </li>
      
      <li class="box  box-delta">
        <section class="box-content">
          <header class="box-header"></header>
          <footer class="box-footer"></footer>
        </section>
      </li>
    </ul>
    


    Я не буду заполнять место в этом посте банальными стилями, которые отражают и позиционируют блоки. Обозначу лишь идентичное во всех примерах появление окружности в самом центре:
    .boxes:after {
        background: none repeat scroll 0 0 #34495E;
        border: 0.5em solid #2C3D50;
        border-radius: 50%;
        content: " ";
        height: 5.5em;
        left: 50%;
        margin-left: -2.75em;
        position: absolute;
        top: 10.75em;
        width: 5.5em;
    }
    

    Первый способ: радиальный градиент


    Необходимо задать правильные радиальные градиенты с цветом и прозрачностью всем footer элементам для верхних блоков и всех header элементам для нижних блоков. На первый взгляд это самый тривиальный способ решения, но в нем есть изюминка, которая заключается в использовании CSS calc() для придания отзывчивости:
    .box-alpha .box-footer {
        background: radial-gradient(circle at calc(100% + 2em) 7em , rgba(0, 0, 0, 0) 6em, #148F77 6em) repeat scroll 0 0 rgba(0, 0, 0, 0);
    }
    .box-beta .box-footer {
        background: radial-gradient(circle at -2em 7em , rgba(0, 0, 0, 0) 6em, #25A25A 6em) repeat scroll 0 0 rgba(0, 0, 0, 0);
    }
    .box-gamma .box-header {
        background: radial-gradient(circle at calc(100% + 2em) -2em , rgba(0, 0, 0, 0) 6em, #217DBB 6em) repeat scroll 0 0 rgba(0, 0, 0, 0);
    }
    .box-delta .box-header {
        background: radial-gradient(circle at -2em -2em , rgba(0, 0, 0, 0) 6em, #804399 6em) repeat scroll 0 0 rgba(0, 0, 0, 0);
    }
    


    Второй способ: рамка для псевдо-элементов


    Я думаю, это — самый изобретательный способ решения. Для элементов header и footer в соответствии для каждого из блоков задаем margin в нужную сторону. После чего для каждой секции и header с footer создаем псевдо-элементы:
    .box-alpha .box-content:before {
        border-color: #148F77 rgba(0, 0, 0, 0) rgba(0, 0, 0, 0) #148F77;
        bottom: -7.5em;
        right: -7.5em;
    }
    .box-content:before {
        border: 10em solid #FF0000;
        content: "";
        display: block;
        height: 0;
        position: absolute;
        width: 0;
    }
    .box-alpha .box-content:after {
        border-color: #148F77;
        bottom: -10em;
        right: -10em;
    }
    .box-content:after {
        border: 2em solid #FF0000;
        border-radius: 30em;
        content: "";
        display: block;
        height: 12em;
        position: absolute;
        width: 12em;
        z-index: 0;
    }
    
    ... 
    

    Обращаем внимание на :before для .box-alpha, у которого нет ширины или высоты. Вспоминаем как мы все привыкли делать «стрелочки» или треугольники на CSS с помощью border. Тот же принцип и здесь.
    image

    Третий способ: тени и отрицательные отступы


    Мое любимое решение, демонстрирующее доскональное понимание самых тонких нюансов в CSS. Как и в предыдущем варианте для элементов создаем псевдо-элемент и задаем правильный padding. Элемент каждого из блоков, часть которого должна быть обрезана на четверть круга полностью прозрачен. Фон ему задает огромная внешняя тень псевдо-элемента.
    .block__element--cut:before {
        border-radius: 50%;
        content: "";
        height: 8em;
        margin: -5em;
        position: absolute;
        width: 8em;
        z-index: -1;
    }
    .block:nth-child(1) .block__element--cut:before {
        bottom: 0;
        box-shadow: 0 0 0 40em #0F414C;
        right: 0;
    }
    .block:nth-child(2) .block__element--cut:before {
        bottom: 0;
        box-shadow: 0 0 0 40em #673A01;
        left: 0;
    }
    .block:nth-child(3) .block__element--cut:before {
        box-shadow: 0 0 0 40em rgba(187, 169, 255, 0.65);
        right: 0;
        top: 0;
    }
    .block:nth-child(4) .block__element--cut:before {
        box-shadow: 0 0 0 40em rgba(176, 214, 95, 0.65);
        left: 0;
        top: 0;
    }
    

    Но самое элегантное тут вовсе не тень. А использование отрицательного значения margin: -5em. При абсолютном позиционировании right, bottom для элемента будет действовать значения margin-right: -5em и margin-bottom: -5em соответственно.

    Оригинальное решение Hugo на SASS можно посмотреть тут. Он использует box-shadow как в третьем примере. Вспомнив под конец мой вчерашний перевод «Только разработчики 90-х помнят это», хочется сказать, что лет через 15 мы также будет улыбаться на эти дикие методы, которые приведены в этом посту.

    Спасибо всем за внимание.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 16
    • +3
      Недавно начал пытаться учиться верстке HTML+CSS. Могу сказать только одно: жесть!
      • +1
        Начал пытаться или начал учиться?
        • 0
          Можно сказать, уже учусь: за неделю проштудировал на Codecademy курсы по питону, HTML+CSS, сейчас мучаю JavaScript. Блин, интересно то как! Надо второе высшее получать дистанционно :)
      • +1
        класс!
        • 0
          При наличии потомков в блоке, содержимое должно отображаться

          Не очень понятен этот пункт. В потомке разноцветных блоков? Если так, то как он должен отображаться, если содержимого много?
          И вообще если честно задание не очень понятно в плане логики… зачем так извращаться и почему такой частный случай?
          • 0
            codepen.io/hugo/pen/mIvfz
            На верхнем левом блоке в footer пропишите текст, он будет «под цветом» и его не видно.
            • 0
              boxes should be able to contain text; if you come up with a solution that makes this impossible, it's not enough

              Тут скорее лучше перевести «Блоки должны иметь возможность содержать текст, в противном случае ваше решение неверно», тогда смысл понятен.
          • 0
            Последнее очень оригинально!
            • +11
              Напоминает задачу
              Напишите рассказ из слов, начинающихся на букву С.
              Легко, но художественной ценности это не представляет.
              • +3
                Добавьте условие со звёздочкой. *Делать нужно левой рукой стоя на правой ноге сложив губы бантиком, но только обязательно бантиком!
                • 0
                  В первом решении calc() не обязателен. Можно обойтись без него, если у box-footer/box-header отрезать уголки, как во втором способе, а оставшийся квадрат статичной ширины закрашивать градиетом через пвсевдо-элемент.
                  • +9
                    Если честно, мне совсем не импонируют попытки использовать css для отображения графически сложных объектов.
                    Вспомните Гомера Симпсона на css. Да красиво, но абсолютно бесполезно.
                    • 0
                      Угу, тем более что такие извращения на CSS на SVG делаются тривиально, а браузерная поддержка даже лучше (IE9+, и нередко даже можно на IE8 изобразить через Raphaël/SVG или картинку).
                    • 0
                      Еще чуть-чуть и можно будет просто взять и заюзать для этого маску.
                      • 0
                        Жаль что в реальной жизни верстальщика такие гениальные решения все еще разбиваются о заказчиковское «А добавьте еще поддержку IE8+»
                        • 0
                          Для IE8 должен срабатывать fallback. Круг просто пропадет. Начиная IE9 все работает нормально.

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.