От автора: я открыла для себя CSS около десяти лет назад, пытаясь изменить внешний вид блога, который я создала. Довольно скоро я смогла кодировать классные вещи с большим количеством математических и, следовательно, более простых для понимания функций, таких как преобразования. Однако другие области CSS, такие как верстка, остаются постоянным источником боли.
Этот пост посвящен проблеме, с которой я столкнулась около десяти лет назад и до недавнего времени не знала, как ее решить. В частности, речь идет о том, как я нашла решение проблемы, используя современную технику CSS-сетки, которая в процессе дала мне даже более крутые результаты, чем я себе представляла. Это не руководство о том, как лучше всего использовать CSS-сетку, а скорее пошаговое описание моего процесса обучения.
Проблема
Одной из первых вещей, которые я использовал в этом блоге, были случайные фотографии из города, поэтому у меня возникла идея создать сетку миниатюр с фиксированным размером. Для улучшения внешнего вида я хотела, чтобы эта сетка была выровнена по центру относительно абзацев выше и ниже ее, но в то же время я хотела, чтобы миниатюры в последнем ряду были выровнены по левому краю относительно сетки. Между тем, ширина поста (и ширина сетки внутри него) будет зависеть от области просмотра.
HTML выглядит примерно так:
1 2 3 4 5 6 7 8 9 10 |
<section class='post__content'> <p><!-- какой-то текст --></p> <div class='grid--thumbs'> <a href='full-size-image.jpg'> <img src='thumb-image.jpg' alt='image description'/> </a> <!-- другие подобные миниатюры --> </div> <p><!-- другой текст --></p> </section> |
Это может показаться простым, но оказалось, что это одна из самых сложных проблем CSS, с которыми я когда-либо сталкивалась.
Менее чем идеальные решения
Это то, что я пыталась сделать на протяжении многих лет, но это никогда не получалось.
float не подходит
float оказался тупиком, потому что я не могла понять, как сделать так, чтобы сетка была выровнена по центру.
1 2 3 |
.grid--thumbs { overflow: hidden; } .grid--thumbs a { float: left; } |
Демо ниже показывает попытку сделать это с помощью float. Измените размер блока, чтобы увидеть, как он ведет себя при различной ширине области просмотра.
Безумие inline-block
Сначала это казалось лучшей идеей:
1 2 3 |
.grid--thumbs { text-align: center } .grid--thumbs a { display: inline-block } |
Только оказалось, что это не так:
В этом случае последний ряд не выравнивается по левому краю. В определенный момент, благодаря случайному автозаполнению CSS на CodePen, я узнала о вызываемом свойстве text-align-last, которое определяет, как выравнивается последняя строка блока.
К сожалению, text-align-last: left для сетки не стало решением, которое я искала:
В этот момент я действительно решил отказаться от идеи выровненной по середине сетки. Может ли комбинация text-align: justified и text-align-last: left для сетки дать лучший результат?
Ну, оказывается, это не так. То есть, если только в последнем ряду нет единственной миниатюры и промежутки между столбцами не слишком велики. Измените размер блока ниже, чтобы понять, что я имею в виду.
Это то, что я получила два года назад, после девяти лет попыток решения этой проблемы.
Хаки flexbox
Решение flexbox, которое, сначала казалось, сработало, заключалось в добавлении псевдо-элемента ::after для сетки и установки flex: 1, как для миниатюр, так и этого псевдо-элемента:
1 2 3 4 5 6 7 8 9 10 |
.grid--thumbs { display: flex; flex-wrap: wrap; a, &::after { flex: 1; } img { margin: auto; } &:after { content: 'AFTER'; } } |
Демонстрация ниже показывает, как работает этот метод. Я задала для миниатюр и псевдо-элемента ::after пурпурные контуры, чтобы было легче увидеть, что происходит.
Это не совсем то, что я хотел, потому что сетка миниатюр не выровнена по центру. Но это выглядит не так уж и плохо… пока в последнем ряду изображений ровно на одно меньше, чем у других. Однако, как только это изменяется, макет ломается, если в нем пропущено больше элементов или их нет.
Это была одна идея хака. Другой вариант — снова использовать псевдо-элемент, но добавить после миниатюр столько пустых элементов div, сколько есть столбцов, которые мы ожидаем получить. Это число — это то, что мы должны приблизительно вычислить, так как размер миниатюр фиксирован. Вероятно, мы хотим установить максимальную ширину для поста, поскольку текст, растянутый по ширине на весь экран, может визуально утомлять глаза.
Первые пустые элементы будут занимать всю ширину строки, которая не полностью заполнена миниатюрами, а остальные будут переходить на другие строки. Но так как их height равно ноль, это не имеет значения визуально.
Этот метод делает свое дело, но, опять же, он хакерский и все еще не дает точного результата, так как иногда он приводит к большим и некрасиво выглядящим промежуткам между столбцами.
Решение с помощью сетки?
Макет сетки выглядел как ответ, учитывая его название. Проблема состояла в том, что все примеры, которые я видел к тому времени, использовали предопределенное количество столбцов, и это не работает для этого конкретного шаблона, где количество столбцов определяется шириной области просмотра.
В прошлом году, когда я кодировала коллекцию для одного элемента — шаблонов фона на чистом CSS, у меня появилась идея сгенерировать набор медиа-запросов, которые могли бы изменить переменную CSS —n, соответствующую количеству столбцов, используемых для установки grid-template-columns.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$w: 13em; $h: 19em; $f: $h/$w; $n: 7; $g: 1em; --h: #{$f*$w}; display: grid; grid-template-columns: repeat(var(--n, #{$n}), var(--w, #{$w})); grid-gap: $g; place-content: center; @for $i from 1 to $n { @media (max-width: ($n - $i + 1)*$w + ($n - $i + 2)*$g) { --n: #{$n - $i} } } |
В то время я очень гордилась этой идеей, хотя теперь у меня другое мнение. Один медиа-запрос для каждого возможного числа столбцов не совсем идеален, не говоря уже о том, что он не работает так хорошо, когда ширина сетки не равна ширине области просмотра.
Магическое решение
Я наконец-то нашла лучшее решение, работая с сеткой CSS и не понимая, почему функция repeat() не работает в этой конкретной ситуации. Это было так неприятно, что я пошла на MDN, где я случайно и заметила ключевое слово auto-fit, и, хотя я не понимала объяснения, у меня было предчувствие, что оно может помочь с моей старой проблемой, поэтому я отбросил все остальное и взялась за нее. Вот что я получила:
1 2 3 4 5 6 |
.grid--thumbs { display: grid; justify-content: center; grid-gap: .25em; grid-template-columns: repeat(auto-fit, 8em); } |
Я также обнаружила функцию minmax(), которую можно использовать вместо фиксированных размеров для элементов сетки. Я до сих пор не смогла понять, как именно работает minmax() — и чем больше я экспериментирую с ней, тем меньше я ее понимаю — но в этой ситуации похоже, что она создает сетку, а затем растягивает ее столбцы одинаково, пока они не заполнят все доступное пространства:
1 |
grid-template-columns: repeat(auto-fit, minmax(8em, 1fr)); |
Еще одна интересная вещь, которую мы можем сделать — это предотвратить переполнение изображения, когда оно шире элемента сетки. Мы можем сделать это, заменив минимум 8em на min(8em, 100%). Это по существу гарантирует, что изображения никогда не будут превышать 100%, но никогда не будут меньше 8em. Спасибо Крису за это предложение!
Обратите внимание, что функция min() не работает в до-Chromium Edge!
Имейте в виду, что это дает хороший результат, только если все изображения имеют одинаковое соотношение сторон — как квадратные изображения, которые я использовала здесь. Для моего блога это не было проблемой, поскольку все фотографии были сделаны на моем телефоне Sony Ericsson W800i, и все они имели одинаковое соотношение сторон. Но если бы мы использовали изображения с разными соотношениями сторон, сетка уже не выглядела бы так хорошо:
Конечно, мы можем установить для height изображений фиксированное значение, но это искажает изображения … если мы не установим для object-fit значение cover, которое решает нашу проблему!
Другой идеей было бы превратить первую миниатюру в своего рода баннер, который охватывает все столбцы сетки. Проблема в том, что мы не знаем количество столбцов, потому что это зависит от области просмотра. Но есть это решение — мы можем установить для grid-column-end значение -1!
1 2 3 4 5 6 7 8 9 |
.grid--thumbs { /* те же стили, что и раньше */ a:first-child { grid-column: 1/ -1; img { height: 13em } } } |
Первое изображение имеет height больше, чем у всех остальных.
Конечно, если бы мы хотели, чтобы изображение охватывало все столбцы, кроме последнего, то мы установили бы -2 и т. д… Отрицательные индексы столбцов — это крутая вещь!
auto-fill — это еще одно ключевое слово свойств сетки, которое я нашла на MDN. Объяснения для обоих — куча текста без визуальных элементов, поэтому я не нашла их особенно полезными. Еще хуже, заменяя auto-fit на auto-fill для любой из демонстраций выше, мы не получаем абсолютно никакой разницы. Как они действительно работают и чем они отличаются, до сих пор остается загадкой, даже после прочтения статей или экспериментов с примерами.
Однако, пробуя разные вещи и видя, что происходит в различных сценариях в одной точке, я пришла к выводу, что, если мы используем ширину столбца minmax(), а не фиксированную (например 8em), то, вероятно, лучше использовать auto-fill вместо auto-fit, потому что, результат выглядит лучше, если у нас всего несколько изображений, как показано на интерактивной демонстрации ниже:
Я думаю, что лично мне больше всего нравится первоначальная идея сетки миниатюр, которая выровнена по центру и имеет в основном фиксированную ширину столбца (но все равно использует min(100%, 15em) вместо просто 15em). В конце концов, это вопрос личных предпочтений, и макет в демонстрации ниже, выглядит для меня лучше:
Я использую в этой демо auto-fit, потому что оно дает тот же результат, что auto-fill, и на один символ короче. Однако, что я не поняла, так это почему оба ключевых слова дают одинаковый результат, когда в галерее больше элементов, чем нам нужно, чтобы заполнить ряд.
Но когда это изменяется, auto-fit и auto-fill дают разные результаты, как показано ниже. Вы можете изменить значение justify-content и количество элементов, размещенных в сетке:
Я не совсем уверена, какой выбор лучше. Я думаю, это также зависит от личных предпочтений. В сочетании с justify-content: center auto-fill кажется, более логичным вариантом, но, в то же время, auto-fit дает лучший результат.
Автор: Ana Tudor
Источник: //css-tricks.com
Редакция: Команда webformyself.