Все, кому приходится иметь дело с вёрсткой, знают что гриды и flexbox давно захватили CSS, позволяют очень удобно организовать классическую выкладку хедер-контент-сайдбар-футер, списки карточек, masonry и так далее. Но их настоящая крутизна не в удобстве использования, а в бескрайних возможностях, которые они открывают. Я покажу и объясню мой любимый трюк, который позволяет верстать за рамками привычной вертикально-горизонтальной прямоугольной сетки, и выглядит это очень круто.
На закуску разберём пример с КДПВ. Сетка на нём всё же прямоугольная, но очевидно, что поворотом на 45 градусов такого эффекта не добиться.
Разберём его строение. Структура HTML не содержит подвохов:
<ul>
<li>
<img src="/book01.jpg" alt="" />
</li>
<li>
<img src="/book02.jpg" alt="" />
</li>
<!-- И так далее -->
</ul>
В CSS же мы обнаружим, что каждый ромб занимает две столбца сетки, а всего столбцов используется 2n+1, где n — число ромбов:
/*
Сначала задаётся дефолтное значение для самых узких экранов
3 столбца = 1 ромб в строке
*/
:root {
--columns: 3;
}
/*
Благодаря значению span 2 каждый ромб занимает два столбца
в ширину, но не в высоту.
*/
li {
grid-column-end: span 2;
}
/*
Медиа-запрос для 4 ромбов в строке определяет уже 9 столбцов
*/
@media (min-width: 1200px) {
:root {
--columns: 9;
}
}
Теперь понятно, что имея в запасе лишнюю «половинку» в каждой строке, мы можем сдвигать чётные строки на половину ширины ромба, чтобы получить шахматный порядок:
/* Двигаем каждый второй элемент при одном столбце */
li:nth-child(2n) {
grid-column-start: 2;
}
/* В других случаях подсчёт тоже простой */
@media (min-width: 1200px) {
li:nth-child(6n-2) {
grid-column-start: auto;
}
li:nth-child(8n-3) {
grid-column-start: 2;
}
}
Таким образом, мы получаем следующую картинку (пунктиром выделены границы строк и столбцов сетки):
Нам осталось только «склеить» строки, избавившись от пустого места между ними, для этого подойдёт простой советский margin-top: -50%. Загадка выкладки на этом заканчивается, а если вам интересно, как обложки книги выныривают из ромбов, то пожалуйста — наглядный пример:
Здесь используются li:before и li:after, форма достигается с помощью clip-path, а полосатая раскраска — с помощью крутого трюка с двойным применением бэкграунда:
background-size: 50% 100%;
background-position: left, right;
background-image: linear-gradient(45deg, var(--pink) 40%, var(--green) 40%),
linear-gradient(-45deg, var(--pink) 40%, var(--green) 40%);
Посмотреть на эту фишку и многие другие безумные трюки можно на A Single Div (репо).
Этот заголовок — подкол в сторону недавней статьи про шестиугольные поля в играх. Статья классная, но если представить себе перенос тех схем в CSS, станет страшно и захочется плакать:
Плакать мы не будем, лучше сделаем выводы из предыдущего примера и попробуем сделать сетку из шестиугольников по-умному. Во время реализации мы задействуем сразу несколько трюков, поэтому этот способ красив не только снаружи, но и изнутри. Зацените рабочий пример и начнём:
Структура HTML почти не изменилась:
<div class="main">
<div class="container">
<div></div>
<div></div>
<div></div>
<!-- И так далее -->
</div>
</div>
Задавать форму шестиугольников будем через clip-path:
Исходные стили у нас простые:
.main {
display: flex;
--s: 100px; /* size */
--m: 4px; /* margin */
}
.container {
font-size: 0; /* убирает пробелы между элементами inline-block */
}
.container div {
width: var(--s);
margin: var(--m);
height: calc(var(--s) * 1.1547);
display: inline-block;
font-size: initial; /* we reset the font-size if we want to add some content */
clip-path: polygon(0% 25%, 0% 75%, 50% 100%, 100% 75%, 100% 25%, 50% 0%);
}
Для выкладки мы используем inline-block, в котором, чтобы избежать лишних пробелов, используем трюк с font-size: 0. Отступы между элементами контролирует переменная --m (от margin). Теперь добавим отрицательный отступ, чтобы элементы приняли нужную высоту:
.container div {
...
margin-bottom: calc(var(--m) - var(--s) * 0.2886);
}
Элементы выстраиваются в обычную сетку и накладываются друг на друга, пора заняться смещением. Слева мы разместим container::before с высотой контейнера и шириной половины элемента плюс отступа:
Теперь прибегнем к крутейшему свойству shape-outside, которое позволяет тексту обтекать произвольные формы (а не только прямоугольные по модели margin-box). Крутизна в том, что это работает не только с текстом, но и с любыми inline-элементами. Чтобы создать нужную нам форму обтекания (не затрагивает нечётные ряды, чётные сдвигает на определённое расстояние), воспользуемся второй мощной фишкой — shape-outside принимает градиенты, что позволяет создать повторяющийся паттерн:
shape-outside: repeating-linear-gradient(#0000 0 A, #000 0 B);
Осталось определить A и B. B будет равняться высоте двух рядов, потому что нам нужно, чтобы паттерн повторялся каждые два ряда, это аналогично высоте двух шестиугольников с отступами, минус удвоенная высота их пересечения:
calc(1.732 * var(--s) + 4 * var(--m))
Запишем это выражение в новую переменную --f. По сути, A равно B, только нам нужно вычесть несколько пикселей, чтобы появилось несколько прозрачных пикселей, которые и сдвинут весь ряд направо. В итоге получаем:
shape-outside: repeating-linear-gradient(#0000 0 calc(var(--f) - 3px),#000 0 var(--f));
Круто? Круто. Подведём итог:
.main {
display:flex;
--s: 100px; /* size */
--m: 4px; /* margin */
/* Дополнительно вычитаем 1px, чтобы сгладить ошибки округления */
--f: calc(var(--s) * 1.732 + 4 * var(--m) - 1px);
}
.container {
font-size: 0; /* disable white space between inline block element */
}
.container div {
width: var(--s);
margin: var(--m);
height: calc(var(--s) * 1.1547);
display: inline-block;
font-size:initial;
clip-path: polygon(0% 25%, 0% 75%, 50% 100%, 100% 75%, 100% 25%, 50% 0%);
margin-bottom: calc(var(--m) - var(--s) * 0.2885);
}
.container::before {
content: "";
width: calc(var(--s) / 2 + var(--m));
float: left;
height: 120%;
shape-outside: repeating-linear-gradient(#0000 0 calc(var(--f) - 3px), #000 0 var(--f));
}
Немного CSS-магии, и мы получаем рабочую, адаптивную шестиугольную выкладку! Вообще CSS всех бесит, но иногда он бывает удивительно красив.
Источник статьи: https://habr.com/ru/company/vdsina/blog/561486/
Разминка
На закуску разберём пример с КДПВ. Сетка на нём всё же прямоугольная, но очевидно, что поворотом на 45 градусов такого эффекта не добиться.
Разберём его строение. Структура HTML не содержит подвохов:
<ul>
<li>
<img src="/book01.jpg" alt="" />
</li>
<li>
<img src="/book02.jpg" alt="" />
</li>
<!-- И так далее -->
</ul>
В CSS же мы обнаружим, что каждый ромб занимает две столбца сетки, а всего столбцов используется 2n+1, где n — число ромбов:
/*
Сначала задаётся дефолтное значение для самых узких экранов
3 столбца = 1 ромб в строке
*/
:root {
--columns: 3;
}
/*
Благодаря значению span 2 каждый ромб занимает два столбца
в ширину, но не в высоту.
*/
li {
grid-column-end: span 2;
}
/*
Медиа-запрос для 4 ромбов в строке определяет уже 9 столбцов
*/
@media (min-width: 1200px) {
:root {
--columns: 9;
}
}
Теперь понятно, что имея в запасе лишнюю «половинку» в каждой строке, мы можем сдвигать чётные строки на половину ширины ромба, чтобы получить шахматный порядок:
/* Двигаем каждый второй элемент при одном столбце */
li:nth-child(2n) {
grid-column-start: 2;
}
/* В других случаях подсчёт тоже простой */
@media (min-width: 1200px) {
li:nth-child(6n-2) {
grid-column-start: auto;
}
li:nth-child(8n-3) {
grid-column-start: 2;
}
}
Таким образом, мы получаем следующую картинку (пунктиром выделены границы строк и столбцов сетки):
Нам осталось только «склеить» строки, избавившись от пустого места между ними, для этого подойдёт простой советский margin-top: -50%. Загадка выкладки на этом заканчивается, а если вам интересно, как обложки книги выныривают из ромбов, то пожалуйста — наглядный пример:
Здесь используются li:before и li:after, форма достигается с помощью clip-path, а полосатая раскраска — с помощью крутого трюка с двойным применением бэкграунда:
background-size: 50% 100%;
background-position: left, right;
background-image: linear-gradient(45deg, var(--pink) 40%, var(--green) 40%),
linear-gradient(-45deg, var(--pink) 40%, var(--green) 40%);
Посмотреть на эту фишку и многие другие безумные трюки можно на A Single Div (репо).
Гексагональные миры
Этот заголовок — подкол в сторону недавней статьи про шестиугольные поля в играх. Статья классная, но если представить себе перенос тех схем в CSS, станет страшно и захочется плакать:
Плакать мы не будем, лучше сделаем выводы из предыдущего примера и попробуем сделать сетку из шестиугольников по-умному. Во время реализации мы задействуем сразу несколько трюков, поэтому этот способ красив не только снаружи, но и изнутри. Зацените рабочий пример и начнём:
Структура HTML почти не изменилась:
<div class="main">
<div class="container">
<div></div>
<div></div>
<div></div>
<!-- И так далее -->
</div>
</div>
Задавать форму шестиугольников будем через clip-path:
Исходные стили у нас простые:
.main {
display: flex;
--s: 100px; /* size */
--m: 4px; /* margin */
}
.container {
font-size: 0; /* убирает пробелы между элементами inline-block */
}
.container div {
width: var(--s);
margin: var(--m);
height: calc(var(--s) * 1.1547);
display: inline-block;
font-size: initial; /* we reset the font-size if we want to add some content */
clip-path: polygon(0% 25%, 0% 75%, 50% 100%, 100% 75%, 100% 25%, 50% 0%);
}
Для выкладки мы используем inline-block, в котором, чтобы избежать лишних пробелов, используем трюк с font-size: 0. Отступы между элементами контролирует переменная --m (от margin). Теперь добавим отрицательный отступ, чтобы элементы приняли нужную высоту:
.container div {
...
margin-bottom: calc(var(--m) - var(--s) * 0.2886);
}
Элементы выстраиваются в обычную сетку и накладываются друг на друга, пора заняться смещением. Слева мы разместим container::before с высотой контейнера и шириной половины элемента плюс отступа:
Теперь прибегнем к крутейшему свойству shape-outside, которое позволяет тексту обтекать произвольные формы (а не только прямоугольные по модели margin-box). Крутизна в том, что это работает не только с текстом, но и с любыми inline-элементами. Чтобы создать нужную нам форму обтекания (не затрагивает нечётные ряды, чётные сдвигает на определённое расстояние), воспользуемся второй мощной фишкой — shape-outside принимает градиенты, что позволяет создать повторяющийся паттерн:
shape-outside: repeating-linear-gradient(#0000 0 A, #000 0 B);
Осталось определить A и B. B будет равняться высоте двух рядов, потому что нам нужно, чтобы паттерн повторялся каждые два ряда, это аналогично высоте двух шестиугольников с отступами, минус удвоенная высота их пересечения:
calc(1.732 * var(--s) + 4 * var(--m))
Запишем это выражение в новую переменную --f. По сути, A равно B, только нам нужно вычесть несколько пикселей, чтобы появилось несколько прозрачных пикселей, которые и сдвинут весь ряд направо. В итоге получаем:
shape-outside: repeating-linear-gradient(#0000 0 calc(var(--f) - 3px),#000 0 var(--f));
Круто? Круто. Подведём итог:
.main {
display:flex;
--s: 100px; /* size */
--m: 4px; /* margin */
/* Дополнительно вычитаем 1px, чтобы сгладить ошибки округления */
--f: calc(var(--s) * 1.732 + 4 * var(--m) - 1px);
}
.container {
font-size: 0; /* disable white space between inline block element */
}
.container div {
width: var(--s);
margin: var(--m);
height: calc(var(--s) * 1.1547);
display: inline-block;
font-size:initial;
clip-path: polygon(0% 25%, 0% 75%, 50% 100%, 100% 75%, 100% 25%, 50% 0%);
margin-bottom: calc(var(--m) - var(--s) * 0.2885);
}
.container::before {
content: "";
width: calc(var(--s) / 2 + var(--m));
float: left;
height: 120%;
shape-outside: repeating-linear-gradient(#0000 0 calc(var(--f) - 3px), #000 0 var(--f));
}
Немного CSS-магии, и мы получаем рабочую, адаптивную шестиугольную выкладку! Вообще CSS всех бесит, но иногда он бывает удивительно красив.
Источник статьи: https://habr.com/ru/company/vdsina/blog/561486/